Complete Phase 1: MVC, DB migrations, Auth, RBAC, Security, and Views
This commit is contained in:
143
resources/views/admin/dashboard.php
Normal file
143
resources/views/admin/dashboard.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<div class="dashboard-header">
|
||||
<h1>Welcome, <?= $this->escape($user['name']) ?></h1>
|
||||
<p>Here is your daily intelligence summary for ScoutIQ.</p>
|
||||
</div>
|
||||
|
||||
<!-- Metrics Row -->
|
||||
<div class="metrics-grid">
|
||||
<div class="glass-panel metric-card">
|
||||
<span class="metric-title">Total Investors</span>
|
||||
<span class="metric-value">142</span>
|
||||
<span class="metric-footer">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline><polyline points="17 6 23 6 23 12"></polyline></svg>
|
||||
<span>+12% vs last month</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel metric-card">
|
||||
<span class="metric-title">Total Accelerators</span>
|
||||
<span class="metric-value">38</span>
|
||||
<span class="metric-footer">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline><polyline points="17 6 23 6 23 12"></polyline></svg>
|
||||
<span>+5% vs last month</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel metric-card">
|
||||
<span class="metric-title">Open Opportunities</span>
|
||||
<span class="metric-value">29</span>
|
||||
<span class="metric-footer" style="color: var(--warning);">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
<span>4 closing this week</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel metric-card">
|
||||
<span class="metric-title">Contacts Added Today</span>
|
||||
<span class="metric-value">7</span>
|
||||
<span class="metric-footer">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline><polyline points="17 6 23 6 23 12"></polyline></svg>
|
||||
<span>+3 new organizations</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts Row -->
|
||||
<div class="charts-grid">
|
||||
<div class="glass-panel chart-card">
|
||||
<span class="chart-title">Opportunities by Category</span>
|
||||
<div style="flex: 1; position: relative;">
|
||||
<canvas id="categoryChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel chart-card">
|
||||
<span class="chart-title">Monthly Growth & Ingestion</span>
|
||||
<div style="flex: 1; position: relative;">
|
||||
<canvas id="growthChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Category Chart (Doughnut)
|
||||
const ctxCategory = document.getElementById('categoryChart').getContext('2d');
|
||||
new Chart(ctxCategory, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Mobility & Logistics', 'AI & Automation', 'SaaS', 'Fintech', 'Marketplaces'],
|
||||
datasets: [{
|
||||
data: [35, 25, 20, 12, 8],
|
||||
backgroundColor: [
|
||||
'hsl(263, 90%, 60%)',
|
||||
'hsl(220, 95%, 50%)',
|
||||
'hsl(180, 100%, 40%)',
|
||||
'hsl(142, 70%, 45%)',
|
||||
'hsl(38, 92%, 50%)'
|
||||
],
|
||||
borderWidth: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: 'hsl(215, 20%, 75%)',
|
||||
font: { family: 'Inter', size: 12 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Growth Chart (Line)
|
||||
const ctxGrowth = document.getElementById('growthChart').getContext('2d');
|
||||
new Chart(ctxGrowth, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
datasets: [{
|
||||
label: 'Ingested Opportunities',
|
||||
data: [12, 19, 32, 45, 68, 96],
|
||||
borderColor: 'hsl(180, 100%, 50%)',
|
||||
backgroundColor: 'rgba(0, 242, 254, 0.1)',
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}, {
|
||||
label: 'Interactions Logged',
|
||||
data: [5, 12, 18, 25, 30, 48],
|
||||
borderColor: 'hsl(263, 90%, 60%)',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 3,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: 'hsl(215, 20%, 75%)',
|
||||
font: { family: 'Inter', size: 12 }
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { color: 'rgba(255, 255, 255, 0.05)' },
|
||||
ticks: { color: 'hsl(215, 20%, 70%)' }
|
||||
},
|
||||
y: {
|
||||
grid: { color: 'rgba(255, 255, 255, 0.05)' },
|
||||
ticks: { color: 'hsl(215, 20%, 70%)' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
40
resources/views/auth/login.php
Normal file
40
resources/views/auth/login.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<div class="glass-panel" style="width: 100%; max-width: 440px; padding: 40px; display: flex; flex-direction: column; gap: 30px;">
|
||||
<div style="text-align: center; display: flex; flex-direction: column; gap: 10px;">
|
||||
<h1 style="font-size: 2.2rem; font-weight: 800; background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">ScoutIQ</h1>
|
||||
<p style="color: var(--text-muted); font-size: 0.95rem;">AI-powered Investor Intelligence</p>
|
||||
</div>
|
||||
|
||||
<?php if ($flashError = $this->session->getFlash('error')): ?>
|
||||
<div class="alert alert-error">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 10px;"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
||||
<span><?= $this->escape($flashError) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($flashSuccess = $this->session->getFlash('success')): ?>
|
||||
<div class="alert alert-success">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 10px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
|
||||
<span><?= $this->escape($flashSuccess) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/login" method="POST" style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<input type="hidden" name="_csrf" value="<?= $this->session->getCsrfToken() ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email Address</label>
|
||||
<input type="email" name="email" id="email" class="form-control" placeholder="name@domain.com" required autocomplete="username">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" placeholder="••••••••" required autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 10px;">Sign In</button>
|
||||
</form>
|
||||
|
||||
<div style="text-align: center; font-size: 0.9rem; color: var(--text-muted);">
|
||||
Don't have an account? <a href="/register" style="font-weight: 600;">Sign Up</a>
|
||||
</div>
|
||||
</div>
|
||||
38
resources/views/auth/register.php
Normal file
38
resources/views/auth/register.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<div class="glass-panel" style="width: 100%; max-width: 440px; padding: 40px; display: flex; flex-direction: column; gap: 30px;">
|
||||
<div style="text-align: center; display: flex; flex-direction: column; gap: 10px;">
|
||||
<h1 style="font-size: 2.2rem; font-weight: 800; background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">Create Account</h1>
|
||||
<p style="color: var(--text-muted); font-size: 0.95rem;">Join ScoutIQ Investor Platform</p>
|
||||
</div>
|
||||
|
||||
<?php if ($flashError = $this->session->getFlash('error')): ?>
|
||||
<div class="alert alert-error">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 10px;"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
||||
<span><?= $this->escape($flashError) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/register" method="POST" style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<input type="hidden" name="_csrf" value="<?= $this->session->getCsrfToken() ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="name">Full Name</label>
|
||||
<input type="text" name="name" id="name" class="form-control" placeholder="John Doe" required autocomplete="name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email Address</label>
|
||||
<input type="email" name="email" id="email" class="form-control" placeholder="name@domain.com" required autocomplete="username">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" placeholder="••••••••" required autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 10px;">Create Account</button>
|
||||
</form>
|
||||
|
||||
<div style="text-align: center; font-size: 0.9rem; color: var(--text-muted);">
|
||||
Already have an account? <a href="/login" style="font-weight: 600;">Sign In</a>
|
||||
</div>
|
||||
</div>
|
||||
17
resources/views/errors/403.php
Normal file
17
resources/views/errors/403.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Access Forbidden - ScoutIQ</title>
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
</head>
|
||||
<body style="align-items: center; justify-content: center; display: flex; padding: 20px; min-height: 100vh;">
|
||||
<div class="glass-panel" style="max-width: 500px; width: 100%; padding: 50px; text-align: center; display: flex; flex-direction: column; gap: 30px;">
|
||||
<span style="font-size: 5rem; font-weight: 800; background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-family: 'Outfit';">403</span>
|
||||
<h1 style="font-size: 1.8rem; font-weight: 700;">Access Forbidden</h1>
|
||||
<p style="color: var(--text-muted); font-size: 1rem; line-height: 1.6;">You do not have the required role permissions to access this directory or perform this action.</p>
|
||||
<a href="/" class="btn btn-primary" style="align-self: center;">Return Home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
17
resources/views/errors/404.php
Normal file
17
resources/views/errors/404.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Page Not Found - ScoutIQ</title>
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
</head>
|
||||
<body style="align-items: center; justify-content: center; display: flex; padding: 20px; min-height: 100vh;">
|
||||
<div class="glass-panel" style="max-width: 500px; width: 100%; padding: 50px; text-align: center; display: flex; flex-direction: column; gap: 30px;">
|
||||
<span style="font-size: 5rem; font-weight: 800; background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-family: 'Outfit';">404</span>
|
||||
<h1 style="font-size: 1.8rem; font-weight: 700;">Page Not Found</h1>
|
||||
<p style="color: var(--text-muted); font-size: 1rem; line-height: 1.6;">The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.</p>
|
||||
<a href="/" class="btn btn-primary" style="align-self: center;">Return Home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
17
resources/views/errors/500.php
Normal file
17
resources/views/errors/500.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Error - ScoutIQ</title>
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
</head>
|
||||
<body style="align-items: center; justify-content: center; display: flex; padding: 20px; min-height: 100vh;">
|
||||
<div class="glass-panel" style="max-width: 500px; width: 100%; padding: 50px; text-align: center; display: flex; flex-direction: column; gap: 30px;">
|
||||
<span style="font-size: 5rem; font-weight: 800; background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-family: 'Outfit';">500</span>
|
||||
<h1 style="font-size: 1.8rem; font-weight: 700;">System Server Error</h1>
|
||||
<p style="color: var(--text-muted); font-size: 1rem; line-height: 1.6;"><?= isset($message) ? $this->escape($message) : 'A critical exception has occurred on the application server.' ?></p>
|
||||
<a href="/" class="btn btn-primary" style="align-self: center;">Return Home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
75
resources/views/layouts/admin.php
Normal file
75
resources/views/layouts/admin.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $this->escape($title ?? 'Admin') ?> - ScoutIQ</title>
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
<link rel="stylesheet" href="/assets/css/admin.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-container">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="color: var(--accent);"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>
|
||||
<span>ScoutIQ</span>
|
||||
</div>
|
||||
|
||||
<nav style="flex: 1;">
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item active">
|
||||
<a href="/admin/dashboard">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#crm">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||||
<span>CRM</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#opportunities">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
||||
<span>Opportunities</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="nav-menu">
|
||||
<li class="nav-item">
|
||||
<a href="/logout" style="color: var(--error);">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
|
||||
<span>Logout</span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<!-- Top bar -->
|
||||
<header class="top-bar">
|
||||
<div style="color: var(--text-muted); font-size: 0.9rem;">
|
||||
Platform Mode: <span style="color: var(--accent); font-weight: 600; text-transform: uppercase;"><?= $this->escape($_ENV['APP_ENV'] ?? 'local') ?></span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span style="font-weight: 500; font-size: 0.95rem;"><?= $this->escape($user['name'] ?? 'User') ?></span>
|
||||
<div class="avatar">
|
||||
<?= strtoupper(substr($user['name'] ?? 'U', 0, 1)) ?>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- View content body -->
|
||||
<div class="content-body">
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
14
resources/views/layouts/auth.php
Normal file
14
resources/views/layouts/auth.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $this->escape($title ?? 'Auth') ?> - ScoutIQ</title>
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div style="flex: 1; display: flex; align-items: center; justify-content: center; padding: 20px;">
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user