🚀 Major Release: DigiServer v1.1.0 Production Deployment ## 📁 Project Restructure - Moved all application code to app/ directory for Docker containerization - Centralized persistent data in data/ directory with volume mounting - Removed development artifacts and cleaned up project structure ## 🐳 Docker Integration - Added production-ready Dockerfile with LibreOffice and poppler-utils - Updated docker-compose.yml for production deployment - Added .dockerignore for optimized build context - Created automated deployment script (deploy-docker.sh) - Added cleanup script (cleanup-docker.sh) ## 📄 Document Processing Enhancements - Integrated LibreOffice for professional PPTX to PDF conversion - Implemented PPTX → PDF → 4K JPG workflow for optimal quality - Added poppler-utils for enhanced PDF processing - Simplified PDF conversion to 300 DPI for reliability ## 🔧 File Management Improvements - Fixed absolute path resolution for containerized deployment - Updated all file deletion functions with proper path handling - Enhanced bulk delete functions for players and groups - Improved file upload workflow with consistent path management ## 🛠️ Code Quality & Stability - Cleaned up pptx_converter.py from 442 to 86 lines - Removed all Python cache files (__pycache__/, *.pyc) - Updated file operations for production reliability - Enhanced error handling and logging ## 📚 Documentation Updates - Updated README.md with Docker deployment instructions - Added comprehensive DEPLOYMENT.md guide - Included production deployment best practices - Added automated deployment workflow documentation ## 🔐 Security & Production Features - Environment-based configuration - Health checks and container monitoring - Automated admin user creation - Volume-mounted persistent data - Production logging and error handling ## ✅ Ready for Production - Clean project structure optimized for Docker - Automated deployment with ./deploy-docker.sh - Professional document processing pipeline - Reliable file management system - Complete documentation and deployment guides Access: http://localhost:8880 | Admin: admin/Initial01!
110 lines
4.7 KiB
HTML
110 lines
4.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>Create 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;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="{{ 'dark-mode' if theme == 'dark' else '' }}">
|
|
<div class="container py-5">
|
|
<h1 class="text-center mb-4">Create Group</h1>
|
|
<form method="POST" action="{{ url_for('create_group') }}">
|
|
<div class="row">
|
|
<div class="col-md-6 col-12">
|
|
<div class="mb-3">
|
|
<label for="name" class="form-label">Group Name</label>
|
|
<input type="text" class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" id="name" name="name" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 col-12">
|
|
<div class="mb-3">
|
|
<label for="players" class="form-label">Select Players</label>
|
|
<select multiple class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" id="players" name="players">
|
|
{% for player in players %}
|
|
<option value="{{ player.id }}">{{ player.username }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 col-12">
|
|
<div class="mb-3">
|
|
<label for="orientation" class="form-label">Group Orientation</label>
|
|
<select class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" id="orientation" name="orientation" required>
|
|
<option value="Landscape" selected>Landscape</option>
|
|
<option value="Portret">Portret</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-warning" role="alert">
|
|
<strong>Warning:</strong> Adding players to a group will delete their individual playlists.
|
|
All players in a group will share the same content.
|
|
</div>
|
|
<div id="orientation-warning" class="alert alert-danger d-none" role="alert">
|
|
No players with the selected orientation are available.
|
|
</div>
|
|
<div class="text-center">
|
|
<button type="submit" class="btn btn-primary">Create Group</button>
|
|
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary mt-3">Back to Dashboard</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// Get all players and their orientations from the backend
|
|
const players = [
|
|
{% for player in players %}
|
|
{id: {{ player.id }}, username: "{{ player.username }}", orientation: "{{ player.orientation }}"},
|
|
{% endfor %}
|
|
];
|
|
|
|
const orientationSelect = document.getElementById('orientation');
|
|
const playersSelect = document.getElementById('players');
|
|
const orientationWarning = document.getElementById('orientation-warning');
|
|
|
|
function filterPlayers() {
|
|
const selectedOrientation = orientationSelect.value;
|
|
playersSelect.innerHTML = '';
|
|
let compatibleCount = 0;
|
|
players.forEach(player => {
|
|
if (player.orientation === selectedOrientation) {
|
|
const option = document.createElement('option');
|
|
option.value = player.id;
|
|
option.textContent = player.username;
|
|
playersSelect.appendChild(option);
|
|
compatibleCount++;
|
|
}
|
|
});
|
|
document.getElementById('orientation-warning').classList.toggle('d-none', compatibleCount > 0);
|
|
}
|
|
|
|
orientationSelect.addEventListener('change', filterPlayers);
|
|
|
|
// Initial filter on page load
|
|
filterPlayers();
|
|
</script>
|
|
</body>
|
|
</html> |