Files
RoadmapMaker/templates/admin.html
2025-09-07 08:38:00 +02:00

582 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin - Roadmap Manager</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f8f9fa;
color: #2c3e50;
}
.sidebar {
position: fixed;
left: 0;
top: 0;
width: 250px;
height: 100vh;
background: linear-gradient(135deg, #2c3e50, #3498db);
color: white;
padding: 20px;
z-index: 1000;
}
.sidebar h2 {
margin-bottom: 30px;
text-align: center;
padding-bottom: 20px;
border-bottom: 1px solid rgba(255,255,255,0.2);
}
.nav-item {
padding: 15px 20px;
margin: 5px 0;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 10px;
}
.nav-item:hover, .nav-item.active {
background: rgba(255,255,255,0.1);
transform: translateX(5px);
}
.main-content {
margin-left: 250px;
padding: 30px;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 15px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-primary:hover {
background: #2980b9;
transform: translateY(-2px);
}
.btn-success {
background: #2ecc71;
color: white;
}
.btn-success:hover {
background: #27ae60;
}
.btn-danger {
background: #e74c3c;
color: white;
}
.btn-danger:hover {
background: #c0392b;
}
.btn-warning {
background: #f39c12;
color: white;
}
.card {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
margin-bottom: 25px;
}
.card h3 {
margin-bottom: 20px;
color: #2c3e50;
display: flex;
align-items: center;
gap: 10px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #2c3e50;
}
.form-control {
width: 100%;
padding: 12px 16px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.milestone-item {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
transition: all 0.3s ease;
}
.milestone-item:hover {
border-color: #3498db;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.milestone-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 15px;
}
.milestone-title {
font-weight: 600;
color: #2c3e50;
flex-grow: 1;
}
.milestone-actions {
display: flex;
gap: 10px;
}
.status-badge {
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
}
.badge-completed {
background: #d5f4e6;
color: #27ae60;
}
.badge-in_progress {
background: #fdeaa7;
color: #f39c12;
}
.badge-pending {
background: #ecf0f1;
color: #95a5a6;
}
.modal {
display: none;
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
backdrop-filter: blur(5px);
}
.modal-content {
background: white;
margin: 3% auto;
padding: 30px;
border-radius: 15px;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
animation: modalSlide 0.3s ease-out;
}
@keyframes modalSlide {
from { opacity: 0; transform: translateY(-50px); }
to { opacity: 1; transform: translateY(0); }
}
.close {
position: absolute;
right: 20px;
top: 20px;
font-size: 28px;
cursor: pointer;
color: #aaa;
}
.close:hover {
color: #e74c3c;
}
.section {
display: none;
}
.section.active {
display: block;
}
.back-to-site {
position: fixed;
top: 30px;
right: 30px;
z-index: 1100;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.sidebar {
width: 100%;
height: auto;
position: relative;
}
.main-content {
margin-left: 0;
}
.grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="sidebar">
<h2><i class="fas fa-cogs"></i> Admin Panel</h2>
<div class="nav-item active" data-section="milestones">
<i class="fas fa-list"></i>
Gestione Milestone
</div>
<div class="nav-item" data-section="settings">
<i class="fas fa-cog"></i>
Impostazioni Progetto
</div>
</div>
<div class="main-content">
<div class="header">
<h1><i class="fas fa-tachometer-alt"></i> Dashboard Admin</h1>
<a href="/" class="btn btn-primary">
<i class="fas fa-eye"></i> Visualizza Roadmap
</a>
</div>
<!-- Sezione Milestone -->
<div id="milestones" class="section active">
<div class="card">
<h3><i class="fas fa-plus"></i> Aggiungi Nuova Milestone</h3>
<form id="milestoneForm">
<div class="grid">
<div class="form-group">
<label for="title">Titolo</label>
<input type="text" id="title" class="form-control" required>
</div>
<div class="form-group">
<label for="order">Ordine</label>
<input type="number" id="order" class="form-control" value="{{ milestones|length + 1 }}" required>
</div>
</div>
<div class="form-group">
<label for="description">Descrizione Breve</label>
<input type="text" id="description" class="form-control" required>
</div>
<div class="form-group">
<label for="detailed_description">Descrizione Dettagliata</label>
<textarea id="detailed_description" class="form-control" rows="4"></textarea>
</div>
<div class="form-group">
<label for="status">Stato</label>
<select id="status" class="form-control">
<option value="pending">In Attesa</option>
<option value="in_progress">In Corso</option>
<option value="completed">Completato</option>
</select>
</div>
<button type="submit" class="btn btn-success">
<i class="fas fa-save"></i> Salva Milestone
</button>
</form>
</div>
<div class="card">
<h3><i class="fas fa-tasks"></i> Milestone Esistenti</h3>
<div id="milestonesList">
{% for milestone in milestones %}
<div class="milestone-item" data-id="{{ milestone.id }}">
<div class="milestone-header">
<div class="milestone-title">{{ milestone.order }}. {{ milestone.title }}</div>
<div class="milestone-actions">
<span class="status-badge badge-{{ milestone.status }}">
{% if milestone.status == 'completed' %}Completato
{% elif milestone.status == 'in_progress' %}In Corso
{% else %}In Attesa{% endif %}
</span>
<button class="btn btn-warning btn-sm" onclick="editMilestone({{ milestone.id }})">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-danger btn-sm" onclick="deleteMilestone({{ milestone.id }})">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<p><strong>Descrizione:</strong> {{ milestone.description }}</p>
{% if milestone.detailed_description %}
<p><strong>Dettagli:</strong> {{ milestone.detailed_description[:100] }}...</p>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Sezione Impostazioni -->
<div id="settings" class="section">
<div class="card">
<h3><i class="fas fa-sliders-h"></i> Impostazioni Progetto</h3>
<form id="settingsForm">
<div class="form-group">
<label for="project_name">Nome Progetto</label>
<input type="text" id="project_name" class="form-control" value="{{ settings.project_name if settings else 'Il Mio Progetto' }}">
</div>
<div class="form-group">
<label for="current_milestone">Milestone Corrente</label>
<select id="current_milestone" class="form-control">
{% for milestone in milestones %}
<option value="{{ milestone.id }}" {% if settings and settings.current_milestone_id == milestone.id %}selected{% endif %}>
{{ milestone.order }}. {{ milestone.title }}
</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Salva Impostazioni
</button>
</form>
</div>
</div>
</div>
<!-- Modal per Modifica Milestone -->
<div id="editModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2><i class="fas fa-edit"></i> Modifica Milestone</h2>
<form id="editMilestoneForm">
<input type="hidden" id="editId">
<div class="grid">
<div class="form-group">
<label for="editTitle">Titolo</label>
<input type="text" id="editTitle" class="form-control" required>
</div>
<div class="form-group">
<label for="editOrder">Ordine</label>
<input type="number" id="editOrder" class="form-control" required>
</div>
</div>
<div class="form-group">
<label for="editDescription">Descrizione Breve</label>
<input type="text" id="editDescription" class="form-control" required>
</div>
<div class="form-group">
<label for="editDetailedDescription">Descrizione Dettagliata</label>
<textarea id="editDetailedDescription" class="form-control" rows="4"></textarea>
</div>
<div class="form-group">
<label for="editStatus">Stato</label>
<select id="editStatus" class="form-control">
<option value="pending">In Attesa</option>
<option value="in_progress">In Corso</option>
<option value="completed">Completato</option>
</select>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end;">
<button type="button" class="btn btn-danger" onclick="closeModal()">Annulla</button>
<button type="submit" class="btn btn-success">
<i class="fas fa-save"></i> Salva Modifiche
</button>
</div>
</form>
</div>
</div>
<script>
// Navigation
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function() {
// Remove active class from all nav items and sections
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
// Add active class to clicked nav item and corresponding section
this.classList.add('active');
document.getElementById(this.dataset.section).classList.add('active');
});
});
// Modal functionality
const modal = document.getElementById('editModal');
const closeBtn = document.getElementsByClassName('close')[0];
function closeModal() {
modal.style.display = 'none';
}
closeBtn.onclick = closeModal;
window.onclick = function(event) {
if (event.target == modal) {
closeModal();
}
}
// Add milestone
document.getElementById('milestoneForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = {
title: document.getElementById('title').value,
description: document.getElementById('description').value,
detailed_description: document.getElementById('detailed_description').value,
order: parseInt(document.getElementById('order').value),
status: document.getElementById('status').value
};
fetch('/api/milestone', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Errore nel salvare la milestone');
}
});
});
// Edit milestone
function editMilestone(id) {
fetch(`/api/milestone/${id}`)
.then(response => response.json())
.then(data => {
document.getElementById('editId').value = data.id;
document.getElementById('editTitle').value = data.title;
document.getElementById('editDescription').value = data.description;
document.getElementById('editDetailedDescription').value = data.detailed_description || '';
document.getElementById('editOrder').value = data.order;
document.getElementById('editStatus').value = data.status;
modal.style.display = 'block';
});
}
// Update milestone
document.getElementById('editMilestoneForm').addEventListener('submit', function(e) {
e.preventDefault();
const id = document.getElementById('editId').value;
const formData = {
title: document.getElementById('editTitle').value,
description: document.getElementById('editDescription').value,
detailed_description: document.getElementById('editDetailedDescription').value,
order: parseInt(document.getElementById('editOrder').value),
status: document.getElementById('editStatus').value
};
fetch(`/api/milestone/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Errore nel salvare le modifiche');
}
});
});
// Delete milestone
function deleteMilestone(id) {
if (confirm('Sei sicuro di voler eliminare questa milestone?')) {
fetch(`/api/milestone/${id}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Errore nell\'eliminare la milestone');
}
});
}
}
// Update settings
document.getElementById('settingsForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = {
project_name: document.getElementById('project_name').value,