- Add orientation parameter support to create_group and edit_group functions - Fix manage_group.html template URL endpoint from 'update_group_content_order' to 'update_group_content_order_route' - Add orientation field and filtering to edit_group.html template with JavaScript functionality - Update group_player_management.py to handle orientation validation in create and edit operations - Fix docker-compose.yml to include build directive and correct volume paths - Update entrypoint.sh to handle fresh deployments without migrations - Ensure orientation consistency across group and player management These changes resolve: - Internal Server Error on manage_group page - Missing orientation parameter in group creation/editing - Template URL endpoint mismatches - Docker deployment issues with fresh installations
225 lines
8.7 KiB
HTML
225 lines
8.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Manage Group</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<style>
|
|
body.dark-mode {
|
|
background-color: #121212;
|
|
color: #ffffff;
|
|
}
|
|
.card.dark-mode {
|
|
background-color: #1e1e1e;
|
|
color: #ffffff;
|
|
}
|
|
.dark-mode label, .dark-mode th, .dark-mode td {
|
|
color: #ffffff;
|
|
}
|
|
@media (max-width: 768px) {
|
|
h1 {
|
|
font-size: 1.5rem;
|
|
}
|
|
.btn {
|
|
font-size: 0.9rem;
|
|
padding: 0.5rem 1rem;
|
|
}
|
|
.card {
|
|
margin-bottom: 1rem;
|
|
}
|
|
}
|
|
|
|
.sortable-list li {
|
|
cursor: move;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.sortable-list li.dragging {
|
|
opacity: 0.5;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.drag-handle {
|
|
cursor: grab;
|
|
color: #aaa;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.drag-over {
|
|
border-top: 2px solid #0d6efd;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="{{ 'dark-mode' if theme == 'dark' else '' }}">
|
|
<div class="container py-5">
|
|
<h1 class="text-center mb-4">Manage Group: {{ group.name }}</h1>
|
|
|
|
<!-- Group Information Card -->
|
|
<div class="card mb-4 {{ 'dark-mode' if theme == 'dark' else '' }}">
|
|
<div class="card-header bg-info text-white">
|
|
<h2>Group Info</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<p><strong>Group Name:</strong> {{ group.name }}</p>
|
|
<p><strong>Number of Players:</strong> {{ group.players|length }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- List of Players in the Group -->
|
|
<div class="card mb-4 {{ 'dark-mode' if theme == 'dark' else '' }}">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h2>Players in Group</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-group">
|
|
{% for player in group.players %}
|
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<strong>{{ player.username }}</strong> ({{ player.hostname }})
|
|
</div>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Manage Media Section -->
|
|
<div class="card mb-4 {{ 'dark-mode' if theme == 'dark' else '' }}">
|
|
<div class="card-header bg-info text-white">
|
|
<h2>Manage Media</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if content %}
|
|
<ul class="list-group sortable-list" id="groupMediaList">
|
|
{% for media in content %}
|
|
<li class="list-group-item d-flex align-items-center {{ 'dark-mode' if theme == 'dark' else '' }}"
|
|
draggable="true"
|
|
data-id="{{ media.id }}"
|
|
data-position="{{ loop.index0 }}">
|
|
<!-- Drag handle -->
|
|
<div class="drag-handle me-2" title="Drag to reorder">
|
|
<i class="bi bi-grip-vertical"></i>
|
|
☰
|
|
</div>
|
|
|
|
<div class="flex-grow-1">
|
|
<p class="mb-0"><strong>Media Name:</strong> {{ media.file_name }}</p>
|
|
</div>
|
|
<form action="{{ url_for('edit_group_media', group_id=group.id, content_id=media.id) }}" method="post" class="d-flex align-items-center">
|
|
<div class="input-group me-2">
|
|
<span class="input-group-text">seconds</span>
|
|
<input type="number" class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" name="duration" value="{{ media.duration }}" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-warning me-2">Edit</button>
|
|
</form>
|
|
<form action="{{ url_for('delete_group_media', group_id=group.id, content_id=media.id) }}" method="post" style="display:inline;">
|
|
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this media?');">Delete</button>
|
|
</form>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
<!-- Add a save button for the reordering -->
|
|
<button id="saveGroupOrder" class="btn btn-success mt-3">Save Playlist Order</button>
|
|
{% else %}
|
|
<p class="text-center">No media uploaded for this group.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload Media Button -->
|
|
<div class="text-center mb-4">
|
|
<a href="{{ url_for('upload_content', target_type='group', target_id=group.id, return_url=url_for('manage_group', group_id=group.id)) }}" class="btn btn-primary btn-lg">Go to Upload Media</a>
|
|
</div>
|
|
|
|
<!-- Back to Dashboard Button -->
|
|
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary">Back to Dashboard</a>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const groupMediaList = document.getElementById('groupMediaList');
|
|
let draggedItem = null;
|
|
|
|
// Initialize drag events for all items
|
|
const items = groupMediaList.querySelectorAll('li');
|
|
items.forEach(item => {
|
|
// Drag start
|
|
item.addEventListener('dragstart', function(e) {
|
|
draggedItem = item;
|
|
setTimeout(() => {
|
|
item.classList.add('dragging');
|
|
}, 0);
|
|
});
|
|
|
|
// Drag end
|
|
item.addEventListener('dragend', function() {
|
|
item.classList.remove('dragging');
|
|
draggedItem = null;
|
|
updatePositions();
|
|
});
|
|
|
|
// Drag over
|
|
item.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
if (item !== draggedItem) {
|
|
const rect = item.getBoundingClientRect();
|
|
const y = e.clientY - rect.top;
|
|
const height = rect.height;
|
|
|
|
if (y < height / 2) {
|
|
groupMediaList.insertBefore(draggedItem, item);
|
|
} else {
|
|
groupMediaList.insertBefore(draggedItem, item.nextSibling);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Save button click handler
|
|
document.getElementById('saveGroupOrder').addEventListener('click', function() {
|
|
// Collect new order
|
|
const newOrder = [];
|
|
groupMediaList.querySelectorAll('li').forEach((item, index) => {
|
|
newOrder.push({
|
|
id: item.dataset.id,
|
|
position: index
|
|
});
|
|
});
|
|
|
|
// Send to server
|
|
fetch('{{ url_for("update_group_content_order_route", group_id=group.id) }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': '{{ csrf_token() if csrf_token else "" }}'
|
|
},
|
|
body: JSON.stringify({items: newOrder})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Playlist order updated successfully!');
|
|
console.log('Group playlist update successful:', data);
|
|
} else {
|
|
alert('Error updating playlist order: ' + (data.error || 'Unknown error'));
|
|
console.error('Failed to update group playlist:', data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('An error occurred while updating the playlist order.');
|
|
});
|
|
});
|
|
|
|
// Update positions in the UI
|
|
function updatePositions() {
|
|
groupMediaList.querySelectorAll('li').forEach((item, index) => {
|
|
item.dataset.position = index;
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |