roles & tools
This commit is contained in:
parent
b2cba70f79
commit
ee17043571
1
db/migrations/003_add_roles_text_to_profiles.sql
Normal file
1
db/migrations/003_add_roles_text_to_profiles.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `gtm_profiles` ADD `roles_text` TEXT;
|
||||||
1
db/migrations/004_add_tools_text_to_profiles.sql
Normal file
1
db/migrations/004_add_tools_text_to_profiles.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `gtm_profiles` ADD `tools_text` TEXT;
|
||||||
1
db/migrations/005_add_kpi_data_to_profiles.sql
Normal file
1
db/migrations/005_add_kpi_data_to_profiles.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE gtm_profiles ADD COLUMN kpi_data TEXT;
|
||||||
72
generate_kpis.php
Normal file
72
generate_kpis.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$profile_id = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$profile_id) {
|
||||||
|
echo json_encode(['error' => 'Profile ID is missing.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
$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%']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate sample data for the chart
|
||||||
|
$labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
|
||||||
|
$datasets = [
|
||||||
|
[
|
||||||
|
'label' => 'MRR (Actual)',
|
||||||
|
'data' => array_map(fn() => rand(5000, 18000), range(1, 6)),
|
||||||
|
'borderColor' => '#0d6efd',
|
||||||
|
'tension' => 0.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'label' => 'MRR (Target)',
|
||||||
|
'data' => array_map(fn($i) => 15000 + ($i * 1500), range(1, 6)),
|
||||||
|
'borderColor' => '#dc3545',
|
||||||
|
'borderDash' => [5, 5],
|
||||||
|
'tension' => 0.1
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'kpis' => $kpis,
|
||||||
|
'chartData' => [
|
||||||
|
'labels' => $labels,
|
||||||
|
'datasets' => $datasets
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM gtm_profiles WHERE id = ?");
|
||||||
|
$stmt->execute([$profile_id]);
|
||||||
|
$profile = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$profile) {
|
||||||
|
echo json_encode(['error' => 'Profile not found.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$kpi_data = generate_kpi_data($profile);
|
||||||
|
$kpi_data_json = json_encode($kpi_data);
|
||||||
|
|
||||||
|
// Save the generated data to the database
|
||||||
|
$update_stmt = $pdo->prepare("UPDATE gtm_profiles SET kpi_data = ? WHERE id = ?");
|
||||||
|
$update_stmt->execute([$kpi_data_json, $profile_id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'kpi_data' => $kpi_data]);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('KPI Generation Error: ' . $e->getMessage());
|
||||||
|
echo json_encode(['error' => 'Database error during KPI generation.']);
|
||||||
|
}
|
||||||
@ -42,6 +42,9 @@ try {
|
|||||||
$recommendations .= '</ul>';
|
$recommendations .= '</ul>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$update_stmt = $pdo->prepare("UPDATE gtm_profiles SET roles_text = ? WHERE id = ?");
|
||||||
|
$update_stmt->execute([$recommendations, $profile_id]);
|
||||||
|
|
||||||
echo json_encode(['recommendations' => $recommendations]);
|
echo json_encode(['recommendations' => $recommendations]);
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
|
|||||||
@ -42,6 +42,9 @@ try {
|
|||||||
$recommendations .= '</ul>';
|
$recommendations .= '</ul>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$update_stmt = $pdo->prepare("UPDATE gtm_profiles SET tools_text = ? WHERE id = ?");
|
||||||
|
$update_stmt->execute([$recommendations, $profile_id]);
|
||||||
|
|
||||||
echo json_encode(['recommendations' => $recommendations]);
|
echo json_encode(['recommendations' => $recommendations]);
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
|
|||||||
142
profile.php
142
profile.php
@ -28,6 +28,7 @@ if ($profile_id) {
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter&family=Lora:wght@700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter&family=Lora:wght@700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@ -59,6 +60,50 @@ if ($profile_id) {
|
|||||||
<main class="container my-5 pt-5">
|
<main class="container my-5 pt-5">
|
||||||
<h1 class="mb-4">My GTM Profile</h1>
|
<h1 class="mb-4">My GTM Profile</h1>
|
||||||
<?php if ($profile): ?>
|
<?php if ($profile): ?>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">AI-Generated Role Recommendations</h5>
|
||||||
|
<p class="card-text">Get AI-powered suggestions for marketing and sales roles to execute your GTM strategy.</p>
|
||||||
|
<div id="roles-container" class="mt-3">
|
||||||
|
<?php if (!empty($profile['roles_text'])): ?>
|
||||||
|
<?php echo $profile['roles_text']; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<button id="generate-roles-btn" class="btn btn-primary <?php if (!empty($profile['roles_text'])) echo 'd-none'; ?>" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate Roles</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">AI-Recommended Tools & Integrations</h5>
|
||||||
|
<p class="card-text">Discover top industry solutions and integrations to efficiently execute your GTM strategy.</p>
|
||||||
|
<div id="tools-container" class="mt-3">
|
||||||
|
<?php if (!empty($profile['tools_text'])): ?>
|
||||||
|
<?php echo $profile['tools_text']; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<button id="generate-tools-btn" class="btn btn-primary <?php if (!empty($profile['tools_text'])) echo 'd-none'; ?>" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate Tools</button> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
@ -93,28 +138,6 @@ if ($profile_id) {
|
|||||||
<h6 class="card-subtitle mb-2 text-muted">Goals</h6>
|
<h6 class="card-subtitle mb-2 text-muted">Goals</h6>
|
||||||
<p class="card-text"><strong>Growth Goals:</strong> <?php echo htmlspecialchars($profile['goals']); ?></p>
|
<p class="card-text"><strong>Growth Goals:</strong> <?php echo htmlspecialchars($profile['goals']); ?></p>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">AI-Generated Role Recommendations</h5>
|
|
||||||
<p class="card-text">Get AI-powered suggestions for marketing and sales roles to execute your GTM strategy.</p>
|
|
||||||
<button id="generate-roles-btn" class="btn btn-primary" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate Roles</button>
|
|
||||||
<div id="roles-container" class="mt-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">AI-Recommended Tools & Integrations</h5>
|
|
||||||
<p class="card-text">Discover top industry solutions and integrations to efficiently execute your GTM strategy.</p>
|
|
||||||
<button id="generate-tools-btn" class="btn btn-primary" data-profile-id="<?php echo htmlspecialchars($profile_id); ?>">Generate Tools</button>
|
|
||||||
<div id="tools-container" class="mt-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@ -134,18 +157,20 @@ if ($profile_id) {
|
|||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const profileId = "<?php echo $profile_id; ?>";
|
||||||
const generateBtn = document.getElementById('generate-diagram-btn');
|
const generateBtn = document.getElementById('generate-diagram-btn');
|
||||||
const diagramContainer = document.getElementById('diagram-container');
|
const diagramContainer = document.getElementById('diagram-container');
|
||||||
const editorContainer = document.getElementById('diagram-editor-container');
|
const editorContainer = document.getElementById('diagram-editor-container');
|
||||||
const editor = document.getElementById('diagram-editor');
|
const editor = document.getElementById('diagram-editor');
|
||||||
const saveBtn = document.getElementById('save-diagram-btn');
|
const saveBtn = document.getElementById('save-diagram-btn');
|
||||||
const saveStatus = document.getElementById('save-status');
|
const saveStatus = document.getElementById('save-status');
|
||||||
const profileId = generateBtn ? generateBtn.dataset.profileId : null;
|
|
||||||
|
|
||||||
const generateRolesBtn = document.getElementById('generate-roles-btn');
|
const generateRolesBtn = document.getElementById('generate-roles-btn');
|
||||||
const rolesContainer = document.getElementById('roles-container');
|
const rolesContainer = document.getElementById('roles-container');
|
||||||
const generateToolsBtn = document.getElementById('generate-tools-btn');
|
const generateToolsBtn = document.getElementById('generate-tools-btn');
|
||||||
const toolsContainer = document.getElementById('tools-container');
|
const toolsContainer = document.getElementById('tools-container');
|
||||||
|
const generateKpiBtn = document.getElementById('generate-kpi-btn');
|
||||||
|
const kpiContainer = document.getElementById('kpi-container');
|
||||||
|
|
||||||
if (generateRolesBtn) {
|
if (generateRolesBtn) {
|
||||||
generateRolesBtn.addEventListener('click', function () {
|
generateRolesBtn.addEventListener('click', function () {
|
||||||
@ -159,6 +184,7 @@ if ($profile_id) {
|
|||||||
rolesContainer.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
|
rolesContainer.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
|
||||||
} else {
|
} else {
|
||||||
rolesContainer.innerHTML = data.recommendations;
|
rolesContainer.innerHTML = data.recommendations;
|
||||||
|
generateRolesBtn.classList.add('d-none');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -183,11 +209,12 @@ if ($profile_id) {
|
|||||||
toolsContainer.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
|
toolsContainer.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
|
||||||
} else {
|
} else {
|
||||||
toolsContainer.innerHTML = data.recommendations;
|
toolsContainer.innerHTML = data.recommendations;
|
||||||
|
generateToolsBtn.classList.add('d-none');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error generating tools:', error);
|
console.error('Error generating tools:', error);
|
||||||
toolsContainer.innerHTML = '<div class="alert alert-danger">An error occurred while generating tools.</div>';
|
toolsContainerinnerHTML = '<div class="alert alert-danger">An error occurred while generating tools.</div>';
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
generateToolsBtn.disabled = false;
|
generateToolsBtn.disabled = false;
|
||||||
@ -218,6 +245,73 @@ if ($profile_id) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderKpiDashboard = (kpiData) => {
|
||||||
|
if (!kpiData) return;
|
||||||
|
|
||||||
|
let kpiHtml = '<div class="row">';
|
||||||
|
kpiData.kpis.forEach(kpi => {
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
kpiHtml += '</div>';
|
||||||
|
|
||||||
|
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, {
|
||||||
|
type: 'line',
|
||||||
|
data: kpiData.chartData,
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const existingKpiData = <?php echo $profile['kpi_data'] ?? 'null'; ?>;
|
||||||
|
if (existingKpiData) {
|
||||||
|
renderKpiDashboard(existingKpiData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateKpiBtn) {
|
||||||
|
generateKpiBtn.addEventListener('click', function() {
|
||||||
|
kpiContainer.innerHTML = '<div class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div><p>Generating KPI Dashboard...</p></div>';
|
||||||
|
this.disabled = true;
|
||||||
|
|
||||||
|
fetch(`generate_kpis.php?id=${profileId}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
kpiContainer.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
|
||||||
|
} else {
|
||||||
|
renderKpiDashboard(data.kpi_data);
|
||||||
|
this.classList.add('d-none');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error generating KPIs:', error);
|
||||||
|
kpiContainer.innerHTML = '<div class="alert alert-danger">An error occurred while generating the KPI dashboard.</div>';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if(generateKpiBtn) generateKpiBtn.disabled = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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); ?>;
|
||||||
if (existingDiagram) {
|
if (existingDiagram) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user