Files
web-petting/templates/admin/media_upload.html
T

170 lines
7.3 KiB
HTML
Raw Normal View History

2026-04-29 17:49:07 +03:00
{% extends "admin/layout.html" %}
{% let active_page = "media" %}
{% block title %}{{ t.media_upload_title }}{% endblock %}
{% block content %}
<div class="page-head">
2026-08-09 03:19:47 +01:00
<h1>{{ t.media_title }}</h1>
2026-04-29 17:49:07 +03:00
</div>
2026-08-09 03:19:47 +01:00
<section class="admin-section">
<header class="admin-section-head">
<span class="admin-section-icon" aria-hidden="true">📤</span>
<div>
<h2>{{ t.media_upload_title }}</h2>
<p>{{ client_name }} · {{ visit_label }}</p>
</div>
</header>
<div class="admin-section-body">
2026-04-29 17:49:07 +03:00
<form id="uploadForm" action="/admin/media/{{ visit_id }}/upload/submit" enctype="multipart/form-data">
2026-04-29 17:49:07 +03:00
<div class="field">
<label class="label">{{ t.media_choose_files }}</label>
<div class="control">
<input class="input" type="file" id="uploadFiles" name="files" multiple accept="image/*,video/*" required>
2026-04-29 17:49:07 +03:00
</div>
2026-08-09 03:19:47 +01:00
<p id="fileCount" class="field-help"></p>
2026-08-09 01:05:47 +01:00
<div id="uploadQueue" class="upload-queue"></div>
2026-04-29 17:49:07 +03:00
</div>
<div class="field">
<label class="label">{{ t.media_caption }}</label>
<div class="control">
<input class="input" type="text" name="caption" placeholder="{{ t.media_caption }}">
</div>
</div>
<!-- Progress -->
2026-08-09 03:19:47 +01:00
<div id="uploadProgress" class="upload-progress">
<div class="upload-progress-head">
<span id="uploadStatusText">{{ t.media_upload }}...</span>
<span id="uploadPercent">0%</span>
</div>
2026-08-09 03:19:47 +01:00
<div class="upload-progress-track">
<div id="uploadBar" class="upload-progress-bar"></div>
</div>
</div>
<button type="submit" id="uploadSubmit" class="button is-primary is-fullwidth">{{ t.media_upload }}</button>
2026-04-29 17:49:07 +03:00
</form>
<script>
(function() {
var form = document.getElementById('uploadForm');
var filesInput = document.getElementById('uploadFiles');
var fileCount = document.getElementById('fileCount');
2026-08-09 01:05:47 +01:00
var queue = document.getElementById('uploadQueue');
var progress = document.getElementById('uploadProgress');
var bar = document.getElementById('uploadBar');
var percent = document.getElementById('uploadPercent');
var statusText = document.getElementById('uploadStatusText');
var submitBtn = document.getElementById('uploadSubmit');
filesInput.addEventListener('change', function() {
var n = this.files.length;
2026-08-09 01:05:47 +01:00
fileCount.textContent = n > 0 ? ('{{ t.media_files_selected }}: ' + n) : '';
queue.replaceChildren();
Array.from(this.files).forEach(function(file) {
var item = document.createElement('div');
item.className = 'upload-queue-item';
var icon = document.createElement('span');
icon.className = 'upload-queue-icon';
icon.textContent = file.type.indexOf('video/') === 0 ? '🎬' : '🖼️';
var details = document.createElement('div');
var name = document.createElement('div');
name.className = 'upload-queue-name';
name.textContent = file.name;
var state = document.createElement('div');
state.className = 'upload-queue-state';
state.textContent = '0%';
var track = document.createElement('div');
track.className = 'upload-queue-track';
var itemBar = document.createElement('div');
itemBar.className = 'upload-queue-bar';
track.appendChild(itemBar);
details.append(name, state, track);
item.append(icon, details);
queue.appendChild(item);
});
});
2026-08-09 01:05:47 +01:00
function updateQueue(state, progressValue, processing) {
queue.querySelectorAll('.upload-queue-item').forEach(function(item) {
item.querySelector('.upload-queue-state').textContent = state;
var itemBar = item.querySelector('.upload-queue-bar');
itemBar.classList.toggle('is-processing', processing);
if (!processing) itemBar.style.width = progressValue + '%';
});
}
form.addEventListener('submit', function(e) {
e.preventDefault();
if (!filesInput.files.length) return;
var data = new FormData(form);
var xhr = new XMLHttpRequest();
progress.style.display = 'block';
submitBtn.disabled = true;
2026-08-09 01:05:47 +01:00
submitBtn.textContent = '{{ t.media_upload_sending }}';
statusText.textContent = '{{ t.media_upload_sending }}';
bar.classList.remove('is-processing');
bar.style.width = '0%';
percent.textContent = '0%';
xhr.upload.addEventListener('progress', function(ev) {
if (!ev.lengthComputable) return;
var pct = Math.round(ev.loaded / ev.total * 100);
bar.style.width = pct + '%';
percent.textContent = pct + '%';
2026-08-09 01:05:47 +01:00
updateQueue(pct + '%', pct, false);
if (pct === 100) {
statusText.textContent = '{{ t.media_upload_processing }}';
percent.textContent = '•••';
bar.classList.add('is-processing');
updateQueue('{{ t.media_upload_processing }}', 100, true);
}
});
xhr.upload.addEventListener('load', function() {
statusText.textContent = '{{ t.media_upload_processing }}';
percent.textContent = '•••';
bar.classList.add('is-processing');
updateQueue('{{ t.media_upload_processing }}', 100, true);
});
xhr.addEventListener('load', function() {
if (xhr.status >= 200 && xhr.status < 400) {
bar.style.width = '100%';
2026-08-09 01:05:47 +01:00
bar.classList.remove('is-processing');
percent.textContent = '100%';
2026-08-09 01:05:47 +01:00
statusText.textContent = '{{ t.media_upload_done }}';
updateQueue('{{ t.media_upload_done }}', 100, false);
setTimeout(function() { window.location.href = xhr.responseURL || '/admin/media'; }, 300);
} else {
2026-08-09 01:05:47 +01:00
bar.classList.remove('is-processing');
statusText.textContent = 'Ошибка загрузки (' + xhr.status + ')';
2026-08-09 01:05:47 +01:00
updateQueue('Ошибка загрузки', 0, false);
submitBtn.disabled = false;
submitBtn.textContent = '{{ t.media_upload }}';
}
});
xhr.addEventListener('error', function() {
2026-08-09 01:05:47 +01:00
bar.classList.remove('is-processing');
statusText.textContent = '{{ t.media_upload_connection_error }}';
updateQueue('{{ t.media_upload_connection_error }}', 0, false);
submitBtn.disabled = false;
submitBtn.textContent = '{{ t.media_upload }}';
});
xhr.open('POST', form.action);
xhr.send(data);
});
})();
</script>
2026-08-09 03:19:47 +01:00
</div>
</section>
2026-08-09 01:05:47 +01:00
2026-04-29 17:49:07 +03:00
{% endblock %}