Replace emoji icons with local SVG files for consistent rendering

- Created 10 SVG icon files in app/static/icons/ (Feather Icons style)
- Updated base.html with SVG icons in navigation and dark mode toggle
- Updated dashboard.html with icons in stats cards and quick actions
- Updated content_list_new.html (playlist management) with SVG icons
- Updated upload_media.html with upload-related icons
- Updated manage_player.html with player management icons
- Icons use currentColor for automatic theme adaptation
- Removed emoji dependency for better Raspberry Pi compatibility
- Added ICON_INTEGRATION.md documentation
This commit is contained in:
ske087
2025-11-13 21:00:07 +02:00
parent e5a00d19a5
commit 498c03ef00
37 changed files with 4240 additions and 840 deletions

View File

@@ -0,0 +1,438 @@
{% extends "base.html" %}
{% block title %}Playlist Management - DigiServer v2{% endblock %}
{% block content %}
<style>
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.full-width {
grid-column: 1 / -1;
}
.card-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px 8px 0 0;
margin: -1.5rem -1.5rem 1.5rem -1.5rem;
}
.card-header h2 {
margin: 0;
color: white;
}
.playlist-list {
max-height: 400px;
overflow-y: auto;
}
.playlist-item {
background: #f8f9fa;
padding: 15px;
margin-bottom: 10px;
border-radius: 6px;
border-left: 4px solid #667eea;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s;
}
.playlist-item:hover {
background: #e9ecef;
transform: translateX(5px);
}
.playlist-info h3 {
margin: 0 0 5px 0;
font-size: 18px;
}
.playlist-stats {
font-size: 14px;
color: #6c757d;
}
.playlist-actions {
display: flex;
gap: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
.form-control:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
textarea.form-control {
resize: vertical;
min-height: 80px;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5568d3;
}
.btn-sm {
padding: 5px 10px;
font-size: 13px;
}
.upload-zone {
border: 2px dashed #dee2e6;
border-radius: 8px;
padding: 30px;
text-align: center;
background: #f8f9fa;
transition: all 0.3s;
}
.upload-zone:hover {
border-color: #667eea;
background: #f0f2ff;
}
.upload-zone.drag-over {
border-color: #667eea;
background: #e7e9ff;
}
.file-input-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
}
.file-input-wrapper input[type=file] {
position: absolute;
left: -9999px;
}
.media-library {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
margin-top: 20px;
}
.media-item {
background: white;
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 10px;
text-align: center;
transition: all 0.2s;
cursor: pointer;
}
.media-item:hover {
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.media-icon {
font-size: 48px;
margin-bottom: 10px;
}
.media-name {
font-size: 12px;
word-break: break-word;
}
</style>
<div class="container" style="max-width: 1400px;">
<h1 style="margin-bottom: 25px; display: flex; align-items: center; gap: 0.5rem;">
<img src="{{ url_for('static', filename='icons/playlist.svg') }}" alt="" style="width: 32px; height: 32px;">
Playlist Management
</h1>
<div class="main-grid">
<!-- Create/Manage Playlists Card -->
<div class="card">
<div class="card-header">
<h2 style="display: flex; align-items: center; gap: 0.5rem;">
<img src="{{ url_for('static', filename='icons/playlist.svg') }}" alt="" style="width: 24px; height: 24px; filter: brightness(0) invert(1);">
Playlists
</h2>
</div>
<!-- Create New Playlist Form -->
<form method="POST" action="{{ url_for('content.create_playlist') }}" style="margin-bottom: 25px;">
<h3 style="margin-bottom: 15px;">Create New Playlist</h3>
<div class="form-group">
<label for="playlist_name">Playlist Name *</label>
<input type="text" name="name" id="playlist_name" class="form-control" required
placeholder="e.g., Main Lobby Display">
</div>
<div class="form-group">
<label for="playlist_orientation">Content Orientation *</label>
<select name="orientation" id="playlist_orientation" class="form-control" required>
<option value="Landscape">Landscape (Horizontal)</option>
<option value="Portrait">Portrait (Vertical)</option>
</select>
<small style="color: #6c757d; font-size: 12px; display: block; margin-top: 5px;">
Select the orientation that matches your display screens
</small>
</div>
<div class="form-group">
<label for="playlist_description">Description (Optional)</label>
<textarea name="description" id="playlist_description" class="form-control"
placeholder="Describe the purpose of this playlist..."></textarea>
</div>
<button type="submit" class="btn btn-primary">
Create Playlist
</button>
</form>
<hr style="margin: 25px 0;">
<!-- Existing Playlists -->
<h3 style="margin-bottom: 15px;">Existing Playlists</h3>
<div class="playlist-list">
{% if playlists %}
{% for playlist in playlists %}
<div class="playlist-item">
<div class="playlist-info">
<h3>{{ playlist.name }}</h3>
<div class="playlist-stats">
📊 {{ playlist.content_count }} items |
👥 {{ playlist.player_count }} players |
🔄 v{{ playlist.version }}
</div>
</div>
<div class="playlist-actions">
<a href="{{ url_for('content.manage_playlist_content', playlist_id=playlist.id) }}"
class="btn btn-primary btn-sm">
✏️ Manage
</a>
<form method="POST"
action="{{ url_for('content.delete_playlist', playlist_id=playlist.id) }}"
style="display: inline;"
onsubmit="return confirm('Delete playlist {{ playlist.name }}?');">
<button type="submit" class="btn btn-danger btn-sm" style="display: flex; align-items: center; gap: 0.3rem;">
<img src="{{ url_for('static', filename='icons/trash.svg') }}" alt="" style="width: 14px; height: 14px; filter: brightness(0) invert(1);">
Delete
</button>
</form>
</div>
</div>
{% endfor %}
{% else %}
<div style="text-align: center; padding: 40px; color: #999;">
<img src="{{ url_for('static', filename='icons/playlist.svg') }}" alt="" style="width: 64px; height: 64px; opacity: 0.3; margin-bottom: 10px;">
<p>No playlists yet. Create your first playlist above!</p>
</div>
{% endif %}
</div>
</div>
<!-- Upload Media Card -->
<div class="card">
<div class="card-header">
<h2 style="display: flex; align-items: center; gap: 0.5rem;">
<img src="{{ url_for('static', filename='icons/upload.svg') }}" alt="" style="width: 24px; height: 24px; filter: brightness(0) invert(1);">
Upload Media
</h2>
</div>
<div style="text-align: center; padding: 40px 20px;">
<img src="{{ url_for('static', filename='icons/upload.svg') }}" alt="" style="width: 96px; height: 96px; opacity: 0.5; margin-bottom: 20px;">
<h3 style="margin-bottom: 15px;">Upload Media Files</h3>
<p style="color: #6c757d; margin-bottom: 25px;">
Upload images, videos, and PDFs to your media library.<br>
Assign them to playlists during or after upload.
</p>
<a href="{{ url_for('content.upload_media_page') }}" class="btn btn-success" style="padding: 15px 40px; font-size: 16px; display: inline-flex; align-items: center; gap: 0.5rem;">
<img src="{{ url_for('static', filename='icons/upload.svg') }}" alt="" style="width: 18px; height: 18px; filter: brightness(0) invert(1);">
Go to Upload Page
</a>
</div>
<!-- Media Library Preview -->
<hr style="margin: 25px 0;">
<h3 style="margin-bottom: 15px;">Media Library ({{ media_files|length }} files)</h3>
<div class="media-library">
{% if media_files %}
{% for media in media_files[:12] %}
<div class="media-item" title="{{ media.filename }}">
<div class="media-icon">
{% if media.content_type == 'image' %}
<img src="{{ url_for('static', filename='icons/info.svg') }}" alt="Image" style="width: 48px; height: 48px; opacity: 0.5;">
{% elif media.content_type == 'video' %}
<img src="{{ url_for('static', filename='icons/monitor.svg') }}" alt="Video" style="width: 48px; height: 48px; opacity: 0.5;">
{% elif media.content_type == 'pdf' %}
<img src="{{ url_for('static', filename='icons/info.svg') }}" alt="PDF" style="width: 48px; height: 48px; opacity: 0.5;">
{% else %}
<img src="{{ url_for('static', filename='icons/info.svg') }}" alt="File" style="width: 48px; height: 48px; opacity: 0.5;">
{% endif %}
</div>
<div class="media-name">{{ media.filename[:20] }}...</div>
</div>
{% endfor %}
{% else %}
<div style="text-align: center; padding: 20px; color: #999;">
<p>No media files yet. Upload your first file!</p>
</div>
{% endif %}
</div>
{% if media_files|length > 12 %}
<p style="text-align: center; margin-top: 15px; color: #999;">
+ {{ media_files|length - 12 }} more files
</p>
{% endif %}
</div>
</div>
<!-- Assign Players to Playlists Card -->
<div class="card full-width">
<div class="card-header">
<h2 style="display: flex; align-items: center; gap: 0.5rem;">
<img src="{{ url_for('static', filename='icons/monitor.svg') }}" alt="" style="width: 24px; height: 24px; filter: brightness(0) invert(1);">
Player Assignments
</h2>
</div>
<div style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background: #f8f9fa; text-align: left;">
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Player Name</th>
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Hostname</th>
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Location</th>
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Assigned Playlist</th>
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Status</th>
<th style="padding: 12px; border-bottom: 2px solid #dee2e6;">Actions</th>
</tr>
</thead>
<tbody>
{% for player in players %}
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 12px;"><strong>{{ player.name }}</strong></td>
<td style="padding: 12px;">
<code style="background: #f8f9fa; padding: 2px 6px; border-radius: 3px;">
{{ player.hostname }}
</code>
</td>
<td style="padding: 12px;">{{ player.location or '-' }}</td>
<td style="padding: 12px;">
<form method="POST" action="{{ url_for('content.assign_player_to_playlist', player_id=player.id) }}"
style="display: inline;">
<select name="playlist_id" class="form-control" style="width: auto; display: inline-block;"
onchange="this.form.submit()">
<option value="">No Playlist</option>
{% for playlist in playlists %}
<option value="{{ playlist.id }}"
{% if player.playlist_id == playlist.id %}selected{% endif %}>
{{ playlist.name }}
</option>
{% endfor %}
</select>
</form>
</td>
<td style="padding: 12px;">
{% if player.is_online %}
<span style="background: #28a745; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px;">
🟢 Online
</span>
{% else %}
<span style="background: #6c757d; color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px;">
⚫ Offline
</span>
{% endif %}
</td>
<td style="padding: 12px;">
<a href="{{ url_for('players.player_page', player_id=player.id) }}"
class="btn btn-primary btn-sm">
👁️ View
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
// File upload handling
const fileInput = document.getElementById('file-input');
const uploadZone = document.getElementById('upload-zone');
const fileList = document.getElementById('file-list');
const uploadBtn = document.getElementById('upload-btn');
fileInput.addEventListener('change', handleFiles);
// Drag and drop
uploadZone.addEventListener('dragover', (e) => {
e.preventDefault();
uploadZone.classList.add('drag-over');
});
uploadZone.addEventListener('dragleave', () => {
uploadZone.classList.remove('drag-over');
});
uploadZone.addEventListener('drop', (e) => {
e.preventDefault();
uploadZone.classList.remove('drag-over');
fileInput.files = e.dataTransfer.files;
handleFiles();
});
function handleFiles() {
const files = fileInput.files;
fileList.innerHTML = '';
if (files.length > 0) {
uploadBtn.disabled = false;
const ul = document.createElement('ul');
ul.style.cssText = 'list-style: none; padding: 0;';
for (let file of files) {
const li = document.createElement('li');
li.style.cssText = 'padding: 8px; background: #f8f9fa; margin-bottom: 5px; border-radius: 4px;';
li.textContent = `📎 ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`;
ul.appendChild(li);
}
fileList.appendChild(ul);
} else {
uploadBtn.disabled = true;
}
}
</script>
{% endblock %}