ai-rm-builder-1 / index.html
aceeee's picture
Add 2 files
d62f18c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Department Roadmap Builder</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
.timeline-item:not(:last-child)::after {
content: '';
position: absolute;
left: 24px;
top: 32px;
height: calc(100% - 32px);
width: 2px;
background-color: #e5e7eb;
}
.draggable {
cursor: move;
user-select: none;
transition: transform 0.2s, box-shadow 0.2s;
}
.draggable:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.draggable.dragging {
opacity: 0.5;
transform: scale(0.98);
}
.dropzone {
min-height: 100px;
transition: all 0.3s;
}
.dropzone.active {
background-color: rgba(59, 130, 246, 0.1);
border: 1px dashed #3b82f6;
}
.component-pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.print-only {
display: none;
}
@media print {
.no-print {
display: none !important;
}
.print-only {
display: block !important;
}
body {
background: white;
font-size: 12pt;
}
.container {
width: 100%;
max-width: 100%;
padding: 0;
}
.shadow {
box-shadow: none !important;
}
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="mb-8">
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-gray-800">AI Department Roadmap Builder</h1>
<p class="text-gray-600">Strategic planning tool for AI initiatives</p>
</div>
<div class="flex space-x-4 no-print">
<button id="exportBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center transition-colors">
<i class="fas fa-file-export mr-2"></i> Export
</button>
<button id="printBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg flex items-center transition-colors">
<i class="fas fa-print mr-2"></i> Print
</button>
<button id="saveBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg flex items-center transition-colors">
<i class="fas fa-save mr-2"></i> Save
</button>
<button id="loadBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center transition-colors">
<i class="fas fa-folder-open mr-2"></i> Load
</button>
</div>
</div>
<div class="print-only text-center mb-4">
<h1 class="text-2xl font-bold">AI Department Roadmap</h1>
<p class="text-sm text-gray-600">Generated on: <span id="printDate"></span></p>
</div>
</header>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
<!-- Left Panel - Components -->
<div class="lg:col-span-1 bg-white rounded-xl shadow p-6 no-print">
<h2 class="text-xl font-semibold mb-4 text-gray-800">Roadmap Components</h2>
<div class="space-y-4">
<div class="component-group">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-layer-group mr-2 text-blue-500"></i> Strategic Goals
</h3>
<div class="space-y-2">
<div draggable="true" class="draggable bg-blue-50 border border-blue-200 rounded-lg p-3 text-blue-800" data-type="goal" data-id="goal-1">
<div class="flex items-center">
<i class="fas fa-bullseye mr-2"></i>
<span>Improve AI Model Accuracy</span>
</div>
</div>
<div draggable="true" class="draggable bg-blue-50 border border-blue-200 rounded-lg p-3 text-blue-800" data-type="goal" data-id="goal-2">
<div class="flex items-center">
<i class="fas fa-bullseye mr-2"></i>
<span>Reduce Model Bias</span>
</div>
</div>
<div draggable="true" class="draggable bg-blue-50 border border-blue-200 rounded-lg p-3 text-blue-800" data-type="goal" data-id="goal-3">
<div class="flex items-center">
<i class="fas fa-bullseye mr-2"></i>
<span>Enhance Explainability</span>
</div>
</div>
</div>
</div>
<div class="component-group">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-tasks mr-2 text-green-500"></i> Initiatives
</h3>
<div class="space-y-2">
<div draggable="true" class="draggable bg-green-50 border border-green-200 rounded-lg p-3 text-green-800" data-type="initiative" data-id="initiative-1">
<div class="flex items-center">
<i class="fas fa-project-diagram mr-2"></i>
<span>Model Optimization</span>
</div>
</div>
<div draggable="true" class="draggable bg-green-50 border border-green-200 rounded-lg p-3 text-green-800" data-type="initiative" data-id="initiative-2">
<div class="flex items-center">
<i class="fas fa-project-diagram mr-2"></i>
<span>Data Pipeline Upgrade</span>
</div>
</div>
<div draggable="true" class="draggable bg-green-50 border border-green-200 rounded-lg p-3 text-green-800" data-type="initiative" data-id="initiative-3">
<div class="flex items-center">
<i class="fas fa-project-diagram mr-2"></i>
<span>Ethics Framework</span>
</div>
</div>
</div>
</div>
<div class="component-group">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-calendar-check mr-2 text-purple-500"></i> Milestones
</h3>
<div class="space-y-2">
<div draggable="true" class="draggable bg-purple-50 border border-purple-200 rounded-lg p-3 text-purple-800" data-type="milestone" data-id="milestone-1">
<div class="flex items-center">
<i class="fas fa-flag-checkered mr-2"></i>
<span>Model Validation</span>
</div>
</div>
<div draggable="true" class="draggable bg-purple-50 border border-purple-200 rounded-lg p-3 text-purple-800" data-type="milestone" data-id="milestone-2">
<div class="flex items-center">
<i class="fas fa-flag-checkered mr-2"></i>
<span>Deployment</span>
</div>
</div>
<div draggable="true" class="draggable bg-purple-50 border border-purple-200 rounded-lg p-3 text-purple-800" data-type="milestone" data-id="milestone-3">
<div class="flex items-center">
<i class="fas fa-flag-checkered mr-2"></i>
<span>Audit Completion</span>
</div>
</div>
</div>
</div>
<div class="component-group">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-users mr-2 text-yellow-500"></i> Resources
</h3>
<div class="space-y-2">
<div draggable="true" class="draggable bg-yellow-50 border border-yellow-200 rounded-lg p-3 text-yellow-800" data-type="resource" data-id="resource-1">
<div class="flex items-center">
<i class="fas fa-user-tie mr-2"></i>
<span>Research Team</span>
</div>
</div>
<div draggable="true" class="draggable bg-yellow-50 border border-yellow-200 rounded-lg p-3 text-yellow-800" data-type="resource" data-id="resource-2">
<div class="flex items-center">
<i class="fas fa-server mr-2"></i>
<span>GPU Cluster</span>
</div>
</div>
<div draggable="true" class="draggable bg-yellow-50 border border-yellow-200 rounded-lg p-3 text-yellow-800" data-type="resource" data-id="resource-3">
<div class="flex items-center">
<i class="fas fa-money-bill-wave mr-2"></i>
<span>Budget Allocation</span>
</div>
</div>
</div>
</div>
</div>
<div class="mt-6">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-plus-circle mr-2 text-red-500"></i> Custom Component
</h3>
<div class="flex">
<input type="text" id="customComponent" placeholder="New component name" class="flex-1 border rounded-l-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<button id="addCustomBtn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-r-lg transition-colors">
Add
</button>
</div>
<select id="customType" class="mt-2 w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="goal">Strategic Goal</option>
<option value="initiative">Initiative</option>
<option value="milestone">Milestone</option>
<option value="resource">Resource</option>
</select>
</div>
</div>
<!-- Main Roadmap Area -->
<div class="lg:col-span-3">
<div class="bg-white rounded-xl shadow p-6 mb-6">
<div class="flex justify-between items-center mb-4 no-print">
<h2 class="text-xl font-semibold text-gray-800">AI Department Roadmap</h2>
<div class="flex space-x-2">
<select id="timeframe" class="border rounded-lg px-3 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="quarterly">Quarterly View</option>
<option value="yearly">Yearly View</option>
<option value="3year">3-Year Plan</option>
</select>
<button id="clearAllBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-1 rounded-lg text-sm transition-colors">
Clear All
</button>
</div>
</div>
<div class="overflow-x-auto">
<div id="roadmapTimeline" class="min-w-full">
<!-- Timeline will be generated here -->
<div class="relative">
<!-- Q1 -->
<div class="timeline-item relative pb-8 pl-10">
<div class="absolute left-0 top-0 w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold">
Q1
</div>
<div class="dropzone p-3 rounded-lg border border-dashed border-gray-300" data-timeframe="q1">
<h3 class="font-medium text-gray-700 mb-2">Q1 2024</h3>
<div class="space-y-2" id="q1-items"></div>
</div>
</div>
<!-- Q2 -->
<div class="timeline-item relative pb-8 pl-10">
<div class="absolute left-0 top-0 w-8 h-8 rounded-full bg-green-500 flex items-center justify-center text-white font-bold">
Q2
</div>
<div class="dropzone p-3 rounded-lg border border-dashed border-gray-300" data-timeframe="q2">
<h3 class="font-medium text-gray-700 mb-2">Q2 2024</h3>
<div class="space-y-2" id="q2-items"></div>
</div>
</div>
<!-- Q3 -->
<div class="timeline-item relative pb-8 pl-10">
<div class="absolute left-0 top-0 w-8 h-8 rounded-full bg-yellow-500 flex items-center justify-center text-white font-bold">
Q3
</div>
<div class="dropzone p-3 rounded-lg border border-dashed border-gray-300" data-timeframe="q3">
<h3 class="font-medium text-gray-700 mb-2">Q3 2024</h3>
<div class="space-y-2" id="q3-items"></div>
</div>
</div>
<!-- Q4 -->
<div class="timeline-item relative pb-8 pl-10">
<div class="absolute left-0 top-0 w-8 h-8 rounded-full bg-purple-500 flex items-center justify-center text-white font-bold">
Q4
</div>
<div class="dropzone p-3 rounded-lg border border-dashed border-gray-300" data-timeframe="q4">
<h3 class="font-medium text-gray-700 mb-2">Q4 2024</h3>
<div class="space-y-2" id="q4-items"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Roadmap Summary -->
<div class="bg-white rounded-xl shadow p-6">
<h2 class="text-xl font-semibold mb-4 text-gray-800">Roadmap Summary</h2>
<div id="roadmapSummary" class="space-y-4">
<div class="text-center text-gray-500 py-8">
<i class="fas fa-road text-4xl mb-2 text-gray-300"></i>
<p>Your roadmap summary will appear here</p>
<p class="text-sm">Drag and drop components to build your AI department roadmap</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Save/Load Modal -->
<div id="saveLoadModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl p-6 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold" id="modalTitle">Save Roadmap</h3>
<button id="closeModalBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div id="modalContent">
<!-- Content will be filled dynamically -->
</div>
</div>
</div>
<script>
// Initialize jsPDF
const { jsPDF } = window.jspdf;
document.addEventListener('DOMContentLoaded', function() {
// Set print date
const now = new Date();
document.getElementById('printDate').textContent = now.toLocaleDateString() + ' ' + now.toLocaleTimeString();
// Initialize roadmap state
let roadmapState = {
q1: [],
q2: [],
q3: [],
q4: []
};
// Generate unique ID
function generateId() {
return 'id-' + Math.random().toString(36).substr(2, 9);
}
// Drag and drop functionality
const draggables = document.querySelectorAll('.draggable');
const dropzones = document.querySelectorAll('.dropzone');
let draggedItem = null;
// Add animation to components to encourage interaction
setTimeout(() => {
const components = document.querySelectorAll('.component-group .draggable');
components.forEach((comp, index) => {
setTimeout(() => {
comp.classList.add('component-pulse');
setTimeout(() => {
comp.classList.remove('component-pulse');
}, 2000);
}, index * 300);
});
}, 1000);
// Add event listeners for draggable items
draggables.forEach(item => {
item.addEventListener('dragstart', function(e) {
draggedItem = this;
this.classList.add('dragging');
e.dataTransfer.setData('text/plain', this.dataset.id);
e.dataTransfer.effectAllowed = 'move';
});
item.addEventListener('dragend', function() {
this.classList.remove('dragging');
});
});
// Add event listeners for drop zones
dropzones.forEach(zone => {
zone.addEventListener('dragover', function(e) {
e.preventDefault();
this.classList.add('active');
e.dataTransfer.dropEffect = 'move';
});
zone.addEventListener('dragleave', function() {
this.classList.remove('active');
});
zone.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('active');
if (draggedItem) {
const componentId = e.dataTransfer.getData('text/plain');
const originalComponent = document.querySelector(`[data-id="${componentId}"]`);
if (originalComponent) {
const timeframe = this.dataset.timeframe;
const componentData = {
id: componentId,
type: originalComponent.dataset.type,
text: originalComponent.querySelector('span').textContent,
timeframe: timeframe
};
// Check if component already exists in this timeframe
const existingIndex = roadmapState[timeframe].findIndex(item => item.id === componentId);
if (existingIndex === -1) {
// Add to new timeframe
roadmapState[timeframe].push(componentData);
// Remove from previous timeframe if it exists
Object.keys(roadmapState).forEach(q => {
if (q !== timeframe) {
roadmapState[q] = roadmapState[q].filter(item => item.id !== componentId);
}
});
// Update UI
updateRoadmapUI();
updateRoadmapSummary();
}
}
}
});
});
// Update the roadmap UI based on state
function updateRoadmapUI() {
// Clear all items first
document.querySelectorAll('[id$="-items"]').forEach(container => {
container.innerHTML = '';
});
// Add items based on state
Object.keys(roadmapState).forEach(timeframe => {
const container = document.getElementById(`${timeframe}-items`);
roadmapState[timeframe].forEach(item => {
const component = createRoadmapComponent(item);
container.appendChild(component);
});
});
}
// Create a roadmap component for the timeline
function createRoadmapComponent(item) {
let bgColor, borderColor, textColor, icon;
switch(item.type) {
case 'goal':
bgColor = 'bg-blue-50';
borderColor = 'border-blue-200';
textColor = 'text-blue-800';
icon = 'fa-bullseye';
break;
case 'initiative':
bgColor = 'bg-green-50';
borderColor = 'border-green-200';
textColor = 'text-green-800';
icon = 'fa-project-diagram';
break;
case 'milestone':
bgColor = 'bg-purple-50';
borderColor = 'border-purple-200';
textColor = 'text-purple-800';
icon = 'fa-flag-checkered';
break;
case 'resource':
bgColor = 'bg-yellow-50';
borderColor = 'border-yellow-200';
textColor = 'text-yellow-800';
icon = 'fa-user-tie';
break;
}
const component = document.createElement('div');
component.className = `${bgColor} border ${borderColor} rounded-lg p-3 ${textColor} relative group`;
component.dataset.id = item.id;
component.dataset.type = item.type;
component.innerHTML = `
<div class="flex items-center">
<i class="fas ${icon} mr-2"></i>
<span>${item.text}</span>
<button class="delete-btn ml-auto opacity-0 group-hover:opacity-100 text-red-500 hover:text-red-700 transition-opacity">
<i class="fas fa-times"></i>
</button>
</div>
`;
// Add delete functionality
const deleteBtn = component.querySelector('.delete-btn');
deleteBtn.addEventListener('click', function() {
roadmapState[item.timeframe] = roadmapState[item.timeframe].filter(i => i.id !== item.id);
updateRoadmapUI();
updateRoadmapSummary();
});
return component;
}
// Add custom component
document.getElementById('addCustomBtn').addEventListener('click', function() {
const componentName = document.getElementById('customComponent').value.trim();
const componentType = document.getElementById('customType').value;
if (componentName) {
const componentId = generateId();
let bgColor, borderColor, textColor, icon;
switch(componentType) {
case 'goal':
bgColor = 'bg-blue-50';
borderColor = 'border-blue-200';
textColor = 'text-blue-800';
icon = 'fa-bullseye';
break;
case 'initiative':
bgColor = 'bg-green-50';
borderColor = 'border-green-200';
textColor = 'text-green-800';
icon = 'fa-project-diagram';
break;
case 'milestone':
bgColor = 'bg-purple-50';
borderColor = 'border-purple-200';
textColor = 'text-purple-800';
icon = 'fa-flag-checkered';
break;
case 'resource':
bgColor = 'bg-yellow-50';
borderColor = 'border-yellow-200';
textColor = 'text-yellow-800';
icon = 'fa-user-tie';
break;
}
const newComponent = document.createElement('div');
newComponent.className = `draggable ${bgColor} border ${borderColor} rounded-lg p-3 ${textColor}`;
newComponent.setAttribute('draggable', 'true');
newComponent.setAttribute('data-type', componentType);
newComponent.setAttribute('data-id', componentId);
newComponent.innerHTML = `
<div class="flex items-center">
<i class="fas ${icon} mr-2"></i>
<span>${componentName}</span>
</div>
`;
// Add drag events to the new component
newComponent.addEventListener('dragstart', function(e) {
draggedItem = this;
this.classList.add('dragging');
e.dataTransfer.setData('text/plain', this.dataset.id);
e.dataTransfer.effectAllowed = 'move';
});
newComponent.addEventListener('dragend', function() {
this.classList.remove('dragging');
});
// Add to the appropriate component group
const componentGroups = document.querySelectorAll('.component-group');
componentGroups.forEach(group => {
const heading = group.querySelector('h3');
if (heading.textContent.includes(componentType.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()))) {
group.querySelector('.space-y-2').prepend(newComponent);
}
});
document.getElementById('customComponent').value = '';
// Animate the new component
newComponent.classList.add('component-pulse');
setTimeout(() => {
newComponent.classList.remove('component-pulse');
}, 2000);
}
});
// Update roadmap summary
function updateRoadmapSummary() {
const summaryContainer = document.getElementById('roadmapSummary');
summaryContainer.innerHTML = '';
const quarters = ['q1', 'q2', 'q3', 'q4'];
let hasContent = false;
quarters.forEach(q => {
if (roadmapState[q].length > 0) {
hasContent = true;
const quarterSection = document.createElement('div');
quarterSection.className = 'bg-gray-50 rounded-lg p-4';
const quarterTitle = document.createElement('h3');
quarterTitle.className = 'font-semibold text-lg mb-2';
quarterTitle.textContent = document.querySelector(`[data-timeframe="${q}"] h3`).textContent;
quarterSection.appendChild(quarterTitle);
const itemsList = document.createElement('ul');
itemsList.className = 'space-y-2';
roadmapState[q].forEach(item => {
const listItem = document.createElement('li');
listItem.className = 'flex items-center';
let typeBadge;
switch(item.type) {
case 'goal':
typeBadge = '<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded mr-2">Goal</span>';
break;
case 'initiative':
typeBadge = '<span class="bg-green-100 text-green-800 text-xs px-2 py-1 rounded mr-2">Initiative</span>';
break;
case 'milestone':
typeBadge = '<span class="bg-purple-100 text-purple-800 text-xs px-2 py-1 rounded mr-2">Milestone</span>';
break;
case 'resource':
typeBadge = '<span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded mr-2">Resource</span>';
break;
}
listItem.innerHTML = `
${typeBadge}
<span>${item.text}</span>
`;
itemsList.appendChild(listItem);
});
quarterSection.appendChild(itemsList);
summaryContainer.appendChild(quarterSection);
}
});
if (!hasContent) {
summaryContainer.innerHTML = `
<div class="text-center text-gray-500 py-8">
<i class="fas fa-road text-4xl mb-2 text-gray-300"></i>
<p>Your roadmap summary will appear here</p>
<p class="text-sm">Drag and drop components to build your AI department roadmap</p>
</div>
`;
}
}
// Clear all button
document.getElementById('clearAllBtn').addEventListener('click', function() {
if (confirm('Are you sure you want to clear all items from the roadmap?')) {
Object.keys(roadmapState).forEach(q => {
roadmapState[q] = [];
});
updateRoadmapUI();
updateRoadmapSummary();
}
});
// Export button (PDF)
document.getElementById('exportBtn').addEventListener('click', function() {
// Create a PDF of the roadmap
const roadmapElement = document.getElementById('roadmapTimeline');
html2canvas(roadmapElement, {
scale: 2,
logging: false,
useCORS: true,
allowTaint: true
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF('p', 'mm', 'a4');
const imgWidth = 210; // A4 width in mm
const pageHeight = 295; // A4 height in mm
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('AI-Roadmap-' + new Date().toISOString().slice(0, 10) + '.pdf');
});
});
// Print button
document.getElementById('printBtn').addEventListener('click', function() {
window.print();
});
// Save button
document.getElementById('saveBtn').addEventListener('click', function() {
showSaveModal();
});
// Load button
document.getElementById('loadBtn').addEventListener('click', function() {
showLoadModal();
});
// Timeframe selector
document.getElementById('timeframe').addEventListener('change', function() {
// In a real app, this would change the view of the timeline
// For now, we'll just show a notification
const notification = document.createElement('div');
notification.className = 'fixed bottom-4 right-4 bg-blue-500 text-white px-4 py-2 rounded-lg shadow-lg';
notification.textContent = `View changed to ${this.options[this.selectedIndex].text}`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('opacity-0', 'transition-opacity', 'duration-500');
setTimeout(() => {
notification.remove();
}, 500);
}, 2000);
});
// Modal functionality
const modal = document.getElementById('saveLoadModal');
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
const closeModalBtn = document.getElementById('closeModalBtn');
closeModalBtn.addEventListener('click', function() {
modal.classList.add('hidden');
});
function showSaveModal() {
modalTitle.textContent = 'Save Roadmap';
const content = `
<div class="mb-4">
<label for="roadmapName" class="block text-sm font-medium text-gray-700 mb-1">Roadmap Name</label>
<input type="text" id="roadmapName" class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="My AI Roadmap">
</div>
<div class="flex justify-end space-x-2">
<button id="cancelSaveBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg">Cancel</button>
<button id="confirmSaveBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg">Save</button>
</div>
`;
modalContent.innerHTML = content;
modal.classList.remove('hidden');
document.getElementById('cancelSaveBtn').addEventListener('click', function() {
modal.classList.add('hidden');
});
document.getElementById('confirmSaveBtn').addEventListener('click', function() {
const roadmapName = document.getElementById('roadmapName').value.trim() || 'My AI Roadmap';
const roadmapData = {
name: roadmapName,
date: new Date().toISOString(),
state: roadmapState
};
localStorage.setItem('aiRoadmap', JSON.stringify(roadmapData));
// Show success notification
const notification = document.createElement('div');
notification.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg';
notification.textContent = `Roadmap "${roadmapName}" saved successfully!`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('opacity-0', 'transition-opacity', 'duration-500');
setTimeout(() => {
notification.remove();
}, 500);
}, 2000);
modal.classList.add('hidden');
});
}
function showLoadModal() {
const savedRoadmap = localStorage.getItem('aiRoadmap');
if (!savedRoadmap) {
modalTitle.textContent = 'No Saved Roadmap';
modalContent.innerHTML = `
<div class="text-center py-4">
<i class="fas fa-exclamation-circle text-4xl text-yellow-500 mb-2"></i>
<p>No saved roadmap found.</p>
</div>
<div class="flex justify-end">
<button id="closeEmptyModalBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg">OK</button>
</div>
`;
modal.classList.remove('hidden');
document.getElementById('closeEmptyModalBtn').addEventListener('click', function() {
modal.classList.add('hidden');
});
return;
}
modalTitle.textContent = 'Load Roadmap';
try {
const roadmapData = JSON.parse(savedRoadmap);
const content = `
<div class="mb-4">
<p class="text-sm text-gray-600 mb-1">Saved Roadmap:</p>
<div class="bg-gray-50 p-3 rounded-lg">
<h4 class="font-medium">${roadmapData.name}</h4>
<p class="text-xs text-gray-500">Saved on: ${new Date(roadmapData.date).toLocaleString()}</p>
</div>
</div>
<div class="mb-4">
<p class="text-sm font-medium text-gray-700 mb-1">This will replace your current roadmap. Continue?</p>
</div>
<div class="flex justify-end space-x-2">
<button id="cancelLoadBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg">Cancel</button>
<button id="confirmLoadBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg">Load</button>
</div>
`;
modalContent.innerHTML = content;
modal.classList.remove('hidden');
document.getElementById('cancelLoadBtn').addEventListener('click', function() {
modal.classList.add('hidden');
});
document.getElementById('confirmLoadBtn').addEventListener('click', function() {
roadmapState = roadmapData.state;
updateRoadmapUI();
updateRoadmapSummary();
// Show success notification
const notification = document.createElement('div');
notification.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg';
notification.textContent = `Roadmap "${roadmapData.name}" loaded successfully!`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('opacity-0', 'transition-opacity', 'duration-500');
setTimeout(() => {
notification.remove();
}, 500);
}, 2000);
modal.classList.add('hidden');
});
} catch (e) {
modalTitle.textContent = 'Error Loading Roadmap';
modalContent.innerHTML = `
<div class="text-center py-4">
<i class="fas fa-exclamation-triangle text-4xl text-red-500 mb-2"></i>
<p>Error loading saved roadmap.</p>
</div>
<div class="flex justify-end">
<button id="closeErrorModalBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg">OK</button>
</div>
`;
modal.classList.remove('hidden');
document.getElementById('closeErrorModalBtn').addEventListener('click', function() {
modal.classList.add('hidden');
});
}
}
// Close modal when clicking outside
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.classList.add('hidden');
}
});
// Initialize empty roadmap
updateRoadmapSummary();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=aceeee/ai-rm-builder-1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>