fix dashboard generation

This commit is contained in:
Flatlogic Bot 2025-10-03 19:13:03 +00:00
parent ee17043571
commit 0c9c7ad511
3 changed files with 177 additions and 20 deletions

View File

@ -12,28 +12,72 @@ if (!$profile_id) {
// Basic AI/Rule-based KPI generation // Basic AI/Rule-based KPI generation
function generate_kpi_data($profile) { function generate_kpi_data($profile) {
// In a real scenario, you'd have a more complex logic or an API call to an AI model // Default values
$base_mrr = 5000;
$base_cac = 400;
$base_nrr = 95;
// Adjust KPIs based on profile data
if (isset($profile['organization_size'])) {
switch ($profile['organization_size']) {
case '1-10':
$base_mrr = 5000;
break;
case '11-50':
$base_mrr = 25000;
break;
case '51-200':
$base_mrr = 75000;
break;
case '201+':
$base_mrr = 200000;
break;
}
}
if (isset($profile['market_approach'])) {
if ($profile['market_approach'] === 'Product-led') {
$base_nrr = 110;
$base_cac = 200;
} elseif ($profile['market_approach'] === 'Sales-led') {
$base_nrr = 98;
$base_cac = 800;
}
}
$current_mrr = $base_mrr * (rand(80, 120) / 100);
$target_mrr = $base_mrr * 2;
$current_cac = $base_cac * (rand(90, 130) / 100);
$target_cac = $base_cac * 0.8;
$current_nrr = $base_nrr * (rand(95, 105) / 100);
$target_nrr = $base_nrr * 1.1;
$kpis = [ $kpis = [
['name' => 'Monthly Recurring Revenue (MRR)', 'current' => '$' . number_format(rand(5000, 15000)), 'target' => '$25,000'], ['name' => 'Monthly Recurring Revenue (MRR)', 'current' => number_format($current_mrr), 'target' => number_format($target_mrr)],
['name' => 'Customer Acquisition Cost (CAC)', 'current' => '$' . number_format(rand(300, 800)), 'target' => '$400'], ['name' => 'Customer Acquisition Cost (CAC)', 'current' => number_format($current_cac), 'target' => number_format($target_cac)],
['name' => 'Net Revenue Retention (NRR)', 'current' => rand(95, 110) . '%', 'target' => '115%'] ['name' => 'Net Revenue Retention (NRR)', 'current' => round($current_nrr) . '%', 'target' => round($target_nrr) . '%']
]; ];
// Generate sample data for the chart // Generate sample data for the chart
$labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']; $labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
$mrr_growth_rate = ($target_mrr - $current_mrr) / 5;
$datasets = [ $datasets = [
[ [
'label' => 'MRR (Actual)', 'label' => 'MRR (Actual)',
'data' => array_map(fn() => rand(5000, 18000), range(1, 6)), 'data' => array_map(fn($i) => $current_mrr + ($mrr_growth_rate * $i * (rand(80,120)/100)), range(0, 5)),
'borderColor' => '#0d6efd', 'borderColor' => '#0d6efd',
'tension' => 0.1 'tension' => 0.2
], ],
[ [
'label' => 'MRR (Target)', 'label' => 'MRR (Target)',
'data' => array_map(fn($i) => 15000 + ($i * 1500), range(1, 6)), 'data' => array_map(fn($i) => $current_mrr + ($mrr_growth_rate * $i), range(0, 5)),
'borderColor' => '#dc3545', 'borderColor' => '#dc3545',
'borderDash' => [5, 5], 'borderDash' => [5, 5],
'tension' => 0.1 'tension' => 0.2
] ]
]; ];

View File

@ -93,13 +93,15 @@ if ($profile_id) {
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">AI-Generated KPI Dashboard</h5> <div class="d-flex justify-content-between align-items-center mb-3">
<p class="card-text">Set and refine KPIs, and track them with an auto-generated dashboard.</p> <h5 class="card-title mb-0">AI-Generated KPI Dashboard</h5>
<div id="kpi-container" class="mt-3"> <div>
<?php if (empty($profile['kpi_data'])): ?> <button id="edit-kpi-btn" class="btn btn-secondary btn-sm" style="display: none;">Edit KPIs</button>
<button id="generate-kpi-btn" class="btn btn-primary" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate KPI Dashboard</button> <button id="generate-kpi-btn" class="btn btn-primary <?php if (!empty($profile['kpi_data'])) echo 'd-none'; ?>" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate KPI Dashboard</button>
<?php endif; ?> </div>
</div> </div>
<p class="card-text">Set and refine KPIs, and track them with an auto-generated dashboard.</p>
<div id="kpi-container" class="mt-3"></div>
</div> </div>
</div> </div>
</div> </div>
@ -248,26 +250,36 @@ if ($profile_id) {
const renderKpiDashboard = (kpiData) => { const renderKpiDashboard = (kpiData) => {
if (!kpiData) return; if (!kpiData) return;
let kpiHtml = '<div class="row">'; let kpiHtml = '<form id="kpi-form"><div class="row">';
kpiData.kpis.forEach(kpi => { kpiData.kpis.forEach((kpi, index) => {
kpiHtml += ` kpiHtml += `
<div class="col-md-4"> <div class="col-md-4">
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body">
<h6 class="card-title">${kpi.name}</h6> <h6 class="card-title">${kpi.name}</h6>
<p class="card-text">Current: ${kpi.current} | Target: ${kpi.target}</p> <div class="mb-2">
<label class="form-label">Current</label>
<input type="text" class="form-control" name="kpi_${index}_current" value="${kpi.current}" readonly>
</div>
<div>
<label class="form-label">Target</label>
<input type="text" class="form-control" name="kpi_${index}_target" value="${kpi.target}" readonly>
</div>
</div> </div>
</div> </div>
</div> </div>
`; `;
}); });
kpiHtml += '</div>'; kpiHtml += '</div></form>';
kpiHtml += '<div class="row"><div class="col-12"><canvas id="kpi-chart"></canvas></div></div>'; kpiHtml += '<div class="row"><div class="col-12"><canvas id="kpi-chart"></canvas></div></div>';
kpiContainer.innerHTML = kpiHtml; kpiContainer.innerHTML = kpiHtml;
const ctx = document.getElementById('kpi-chart').getContext('2d'); const ctx = document.getElementById('kpi-chart').getContext('2d');
new Chart(ctx, { if(window.kpiChart instanceof Chart) {
window.kpiChart.destroy();
}
window.kpiChart = new Chart(ctx, {
type: 'line', type: 'line',
data: kpiData.chartData, data: kpiData.chartData,
options: { options: {
@ -279,6 +291,8 @@ if ($profile_id) {
} }
} }
}); });
document.getElementById('edit-kpi-btn').style.display = 'inline-block';
}; };
const existingKpiData = <?php echo $profile['kpi_data'] ?? 'null'; ?>; const existingKpiData = <?php echo $profile['kpi_data'] ?? 'null'; ?>;
@ -311,6 +325,63 @@ if ($profile_id) {
}); });
} }
const editKpiBtn = document.getElementById('edit-kpi-btn');
if (editKpiBtn) {
editKpiBtn.addEventListener('click', function() {
const kpiForm = document.getElementById('kpi-form');
const inputs = kpiForm.getElementsByTagName('input');
const isEditing = this.textContent === 'Save KPIs';
if (isEditing) {
// Save logic
const formData = new FormData(kpiForm);
const kpis = [];
const kpiData = <?php echo $profile['kpi_data'] ?? 'null'; ?>;
kpiData.kpis.forEach((kpi, index) => {
kpis.push({
name: kpi.name,
current: formData.get(`kpi_${index}_current`),
target: formData.get(`kpi_${index}_target`)
});
});
const updatedKpiData = { kpis: kpis };
fetch('save_kpis.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `id=${profileId}&kpi_data=${JSON.stringify(updatedKpiData)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Re-render with new data
renderKpiDashboard(data.updated_kpi_data);
// Toggle back to edit mode
this.textContent = 'Edit KPIs';
for (let input of inputs) {
input.setAttribute('readonly', true);
}
} else {
alert('Error saving KPIs: ' + data.error);
}
})
.catch(error => {
console.error('Error saving KPIs:', error);
alert('An unexpected error occurred while saving KPIs.');
});
} else {
// Edit logic
this.textContent = 'Save KPIs';
for (let input of inputs) {
input.removeAttribute('readonly');
}
}
});
}
// Load existing diagram on page load // Load existing diagram on page load
const existingDiagram = <?php echo json_encode($profile['diagram_mermaid_text'] ?? null); ?>; const existingDiagram = <?php echo json_encode($profile['diagram_mermaid_text'] ?? null); ?>;

42
save_kpis.php Normal file
View File

@ -0,0 +1,42 @@
<?php
require_once 'db/config.php';
header('Content-Type: application/json');
$profile_id = $_POST['id'] ?? null;
$kpi_data_json = $_POST['kpi_data'] ?? null;
if (!$profile_id || !$kpi_data_json) {
echo json_encode(['error' => 'Profile ID or KPI data is missing.']);
exit;
}
try {
$kpi_data = json_decode($kpi_data_json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Invalid JSON format for KPI data.');
}
$pdo = db();
// Fetch existing data to merge, preserving the chart data
$stmt = $pdo->prepare("SELECT kpi_data FROM gtm_profiles WHERE id = ?");
$stmt->execute([$profile_id]);
$existing_data_row = $stmt->fetch(PDO::FETCH_ASSOC);
$existing_data = $existing_data_row ? json_decode($existing_data_row['kpi_data'], true) : [];
// We only want to update the 'kpis' part, not the chart data
$existing_data['kpis'] = $kpi_data['kpis'];
$updated_kpi_data_json = json_encode($existing_data);
$update_stmt = $pdo->prepare("UPDATE gtm_profiles SET kpi_data = ? WHERE id = ?");
$update_stmt->execute([$updated_kpi_data_json, $profile_id]);
echo json_encode(['success' => true, 'message' => 'KPIs updated successfully.', 'updated_kpi_data' => $existing_data]);
} catch (Exception $e) {
error_log('KPI Save Error: ' . $e->getMessage());
echo json_encode(['error' => 'Error saving KPIs: ' . $e->getMessage()]);
}