Add edit modals and image upload for admin dashboard

User Interface Improvements:
- Added edit modal for skills with activate/deactivate checkbox
- Added edit modal for social links with activate/deactivate checkbox
- Skills and social links now default to "active" when created
- Better UX with inline editing instead of separate pages

Image Upload Feature:
- Implemented file upload for project images
- Support for png, jpg, jpeg, gif, webp (max 16 MB)
- Automatic filename sanitization and timestamp prefixing
- Preview of current image in edit mode
- Option to upload file OR enter manual URL
- Files saved to static/img/ directory

Modified Files:
- app.py: Added upload configuration (MAX_CONTENT_LENGTH, UPLOAD_FOLDER, ALLOWED_EXTENSIONS)
- routes/admin.py: Added save_uploaded_file() helper and file handling in project routes
- templates/admin/skills.html: Added edit modal with is_active checkbox
- templates/admin/social_links.html: Added edit modal with is_active checkbox
- templates/admin/project_form.html: Added file upload input with preview

Benefits:
- No more "inactive" items when creating new entries
- Easy toggle of active/inactive state
- Professional image upload with validation
- Better user experience overall
This commit is contained in:
Claude
2025-11-13 15:29:10 +00:00
parent aa2c704bfb
commit 425e66a473
5 changed files with 211 additions and 25 deletions

View File

@@ -6,7 +6,7 @@
{% block content %}
<div class="card">
<div class="card-body">
<form method="POST">
<form method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label for="title" class="form-label">Titolo *</label>
<input type="text" class="form-control" id="title" name="title"
@@ -18,39 +18,65 @@
<textarea class="form-control" id="description" name="description" rows="4" required>{{ project.description if project else '' }}</textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="image_url" class="form-label">URL Immagine</label>
<input type="text" class="form-control" id="image_url" name="image_url"
value="{{ project.image_url if project else '' }}" placeholder="img/project.webp">
<small class="text-muted">Percorso relativo alla cartella static/</small>
<div class="mb-3">
<label class="form-label">Immagine del Progetto</label>
<div class="row">
<div class="col-md-6">
<label for="image_file" class="form-label text-muted small">Upload Immagine</label>
<input type="file" class="form-control" id="image_file" name="image_file" accept="image/*">
<small class="text-muted">Formati supportati: png, jpg, jpeg, gif, webp (max 16 MB)</small>
</div>
<div class="col-md-6">
<label for="image_url" class="form-label text-muted small">Oppure inserisci URL manualmente</label>
<input type="text" class="form-control" id="image_url" name="image_url"
value="{{ project.image_url if project else '' }}" placeholder="img/project.webp">
<small class="text-muted">Percorso relativo alla cartella static/</small>
</div>
</div>
{% if project and project.image_url %}
<div class="mt-2">
<small class="text-muted">Immagine attuale:</small><br>
<img src="{{ url_for('static', filename=project.image_url) }}" alt="{{ project.title }}" style="max-width: 200px; max-height: 150px;" class="img-thumbnail">
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="github_url" class="form-label">URL GitHub</label>
<input type="url" class="form-control" id="github_url" name="github_url"
value="{{ project.github_url if project else '' }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="demo_url" class="form-label">URL Demo</label>
<input type="url" class="form-control" id="demo_url" name="demo_url"
value="{{ project.demo_url if project else '' }}">
</div>
</div>
<div class="col-md-3 mb-3">
<div class="row">
<div class="col-md-4 mb-3">
<label for="display_order" class="form-label">Ordine Visualizzazione</label>
<input type="number" class="form-control" id="display_order" name="display_order"
value="{{ project.display_order if project else 0 }}">
</div>
<div class="col-md-3 mb-3">
<div class="col-md-4 mb-3">
<label for="animation_delay" class="form-label">Delay Animazione</label>
<input type="text" class="form-control" id="animation_delay" name="animation_delay"
value="{{ project.animation_delay if project else '0s' }}" placeholder="0.2s">
</div>
<div class="col-md-4 mb-3">
<div class="form-check mt-4">
<input type="checkbox" class="form-check-input" id="is_published" name="is_published"
{% if not project or project.is_published %}checked{% endif %}>
<label class="form-check-label" for="is_published">
Pubblica il progetto
</label>
</div>
</div>
</div>
<div class="mb-3">
@@ -61,14 +87,6 @@
<small class="text-muted">Formato: Nome:colore, Nome:colore (es: Python:bg-primary, Flask:bg-info)</small>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="is_published" name="is_published"
{% if not project or project.is_published %}checked{% endif %}>
<label class="form-check-label" for="is_published">
Pubblica il progetto
</label>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-gradient">
<i class="fas fa-save me-2"></i>Salva