fix dashboard generation
This commit is contained in:
parent
ee17043571
commit
0c9c7ad511
@ -12,28 +12,72 @@ if (!$profile_id) {
|
||||
|
||||
// Basic AI/Rule-based KPI generation
|
||||
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 = [
|
||||
['name' => 'Monthly Recurring Revenue (MRR)', 'current' => '$' . number_format(rand(5000, 15000)), 'target' => '$25,000'],
|
||||
['name' => 'Customer Acquisition Cost (CAC)', 'current' => '$' . number_format(rand(300, 800)), 'target' => '$400'],
|
||||
['name' => 'Net Revenue Retention (NRR)', 'current' => rand(95, 110) . '%', 'target' => '115%']
|
||||
['name' => 'Monthly Recurring Revenue (MRR)', 'current' => number_format($current_mrr), 'target' => number_format($target_mrr)],
|
||||
['name' => 'Customer Acquisition Cost (CAC)', 'current' => number_format($current_cac), 'target' => number_format($target_cac)],
|
||||
['name' => 'Net Revenue Retention (NRR)', 'current' => round($current_nrr) . '%', 'target' => round($target_nrr) . '%']
|
||||
];
|
||||
|
||||
// Generate sample data for the chart
|
||||
$labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
|
||||
|
||||
$mrr_growth_rate = ($target_mrr - $current_mrr) / 5;
|
||||
|
||||
$datasets = [
|
||||
[
|
||||
'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',
|
||||
'tension' => 0.1
|
||||
'tension' => 0.2
|
||||
],
|
||||
[
|
||||
'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',
|
||||
'borderDash' => [5, 5],
|
||||
'tension' => 0.1
|
||||
'tension' => 0.2
|
||||
]
|
||||
];
|
||||
|
||||
@ -69,4 +113,4 @@ try {
|
||||
} catch (PDOException $e) {
|
||||
error_log('KPI Generation Error: ' . $e->getMessage());
|
||||
echo json_encode(['error' => 'Database error during KPI generation.']);
|
||||
}
|
||||
}
|
||||
93
profile.php
93
profile.php
@ -93,13 +93,15 @@ if ($profile_id) {
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">AI-Generated KPI Dashboard</h5>
|
||||
<p class="card-text">Set and refine KPIs, and track them with an auto-generated dashboard.</p>
|
||||
<div id="kpi-container" class="mt-3">
|
||||
<?php if (empty($profile['kpi_data'])): ?>
|
||||
<button id="generate-kpi-btn" class="btn btn-primary" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate KPI Dashboard</button>
|
||||
<?php endif; ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="card-title mb-0">AI-Generated KPI Dashboard</h5>
|
||||
<div>
|
||||
<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 <?php if (!empty($profile['kpi_data'])) echo 'd-none'; ?>" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate KPI Dashboard</button>
|
||||
</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>
|
||||
@ -248,26 +250,36 @@ if ($profile_id) {
|
||||
const renderKpiDashboard = (kpiData) => {
|
||||
if (!kpiData) return;
|
||||
|
||||
let kpiHtml = '<div class="row">';
|
||||
kpiData.kpis.forEach(kpi => {
|
||||
let kpiHtml = '<form id="kpi-form"><div class="row">';
|
||||
kpiData.kpis.forEach((kpi, index) => {
|
||||
kpiHtml += `
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<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>
|
||||
`;
|
||||
});
|
||||
kpiHtml += '</div>';
|
||||
kpiHtml += '</div></form>';
|
||||
|
||||
kpiHtml += '<div class="row"><div class="col-12"><canvas id="kpi-chart"></canvas></div></div>';
|
||||
kpiContainer.innerHTML = kpiHtml;
|
||||
|
||||
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',
|
||||
data: kpiData.chartData,
|
||||
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'; ?>;
|
||||
@ -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
|
||||
const existingDiagram = <?php echo json_encode($profile['diagram_mermaid_text'] ?? null); ?>;
|
||||
|
||||
42
save_kpis.php
Normal file
42
save_kpis.php
Normal 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()]);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user