Added SEO keywords. Added medua upload indicator. Added Khabarovsk default
Build and Publish / Build and Publish Docker Image (push) Successful in 1m17s
Build and Publish / Build and Publish Docker Image (push) Successful in 1m17s
This commit is contained in:
@@ -70,14 +70,17 @@
|
||||
<!-- Status -->
|
||||
<div class="field">
|
||||
<label class="label">{{ t.schedule_status }}</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="status">
|
||||
<option value="scheduled" {% if visit.status == "scheduled" %}selected{% endif %}>{{ t.visit_status_scheduled }}</option>
|
||||
<option value="completed" {% if visit.status == "completed" %}selected{% endif %}>{{ t.visit_status_completed }}</option>
|
||||
<option value="cancelled" {% if visit.status == "cancelled" %}selected{% endif %}>{{ t.visit_status_cancelled }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="status" id="statusInput" value="{{ visit.status }}">
|
||||
<div class="status-picker">
|
||||
<button type="button" class="status-btn status-btn-scheduled {% if visit.status == "scheduled" %}is-active{% endif %}" data-value="scheduled">
|
||||
<span class="status-btn-icon">📅</span>{{ t.visit_status_scheduled }}
|
||||
</button>
|
||||
<button type="button" class="status-btn status-btn-completed {% if visit.status == "completed" %}is-active{% endif %}" data-value="completed">
|
||||
<span class="status-btn-icon">✅</span>{{ t.visit_status_completed }}
|
||||
</button>
|
||||
<button type="button" class="status-btn status-btn-cancelled {% if visit.status == "cancelled" %}is-active{% endif %}" data-value="cancelled">
|
||||
<span class="status-btn-icon">✕</span>{{ t.visit_status_cancelled }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -150,14 +153,15 @@
|
||||
<div class="upload-modal">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
||||
<h3 style="font-size:1.1rem;font-weight:700;margin:0;">{{ t.media_upload_title }}</h3>
|
||||
<button type="button" style="background:none;border:none;font-size:1.2rem;cursor:pointer;color:#888;" onclick="document.getElementById('uploadModal').classList.remove('is-open')">✕</button>
|
||||
<button type="button" id="uploadModalClose" style="background:none;border:none;font-size:1.2rem;cursor:pointer;color:#888;">✕</button>
|
||||
</div>
|
||||
<form method="post" action="/admin/media/{{ visit.id }}/upload/submit" enctype="multipart/form-data">
|
||||
<form id="uploadForm" action="/admin/media/{{ visit.id }}/upload/submit" enctype="multipart/form-data">
|
||||
<div class="field">
|
||||
<label class="label">{{ t.media_choose_files }}</label>
|
||||
<div class="control">
|
||||
<input class="input" type="file" name="files" multiple accept="image/*,video/*" required>
|
||||
<input class="input" type="file" id="uploadFiles" name="files" multiple accept="image/*,video/*" required>
|
||||
</div>
|
||||
<p id="fileCount" style="font-size:0.8rem;color:#888;margin-top:0.3rem;"></p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ t.media_caption }}</label>
|
||||
@@ -165,12 +169,56 @@
|
||||
<input class="input" type="text" name="caption" placeholder="{{ t.media_caption }}">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="button is-primary is-fullwidth">{{ t.media_upload }}</button>
|
||||
|
||||
<!-- Progress -->
|
||||
<div id="uploadProgress" style="display:none;margin-bottom:1rem;">
|
||||
<div style="display:flex;justify-content:space-between;font-size:0.82rem;color:#555;margin-bottom:0.3rem;">
|
||||
<span id="uploadStatusText">{{ t.media_upload }}...</span>
|
||||
<span id="uploadPercent">0%</span>
|
||||
</div>
|
||||
<div style="background:#e8e8e8;border-radius:99px;height:8px;overflow:hidden;">
|
||||
<div id="uploadBar" style="height:100%;width:0%;background:linear-gradient(90deg,#6c63ff,#b06cff);border-radius:99px;transition:width 0.2s;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="uploadSubmit" class="button is-primary is-fullwidth">{{ t.media_upload }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.status-picker {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.status-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.6rem 0.4rem;
|
||||
border-radius: 10px;
|
||||
border: 2px solid transparent;
|
||||
background: #f5f5f5;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
color: #777;
|
||||
transition: all 0.15s;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.status-btn-icon { font-size: 1.3rem; line-height: 1; }
|
||||
.status-btn:hover { filter: brightness(0.95); }
|
||||
|
||||
.status-btn-scheduled.is-active { background: #dbeafe; border-color: #3b82f6; color: #1e40af; }
|
||||
.status-btn-completed.is-active { background: #d1fae5; border-color: #22c55e; color: #15803d; }
|
||||
.status-btn-cancelled.is-active { background: #fee2e2; border-color: #ef4444; color: #b91c1c; }
|
||||
|
||||
.status-btn-scheduled:not(.is-active):hover { background: #eff6ff; color: #3b82f6; }
|
||||
.status-btn-completed:not(.is-active):hover { background: #f0fdf4; color: #22c55e; }
|
||||
.status-btn-cancelled:not(.is-active):hover { background: #fff5f5; color: #ef4444; }
|
||||
|
||||
.visit-media-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
@@ -229,8 +277,91 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.getElementById('uploadModal').addEventListener('click', function(e) {
|
||||
if (e.target === this) this.classList.remove('is-open');
|
||||
// Status picker
|
||||
document.querySelectorAll('.status-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
document.querySelectorAll('.status-btn').forEach(function(b) { b.classList.remove('is-active'); });
|
||||
btn.classList.add('is-active');
|
||||
document.getElementById('statusInput').value = btn.dataset.value;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var modal = document.getElementById('uploadModal');
|
||||
var form = document.getElementById('uploadForm');
|
||||
var filesInput = document.getElementById('uploadFiles');
|
||||
var fileCount = document.getElementById('fileCount');
|
||||
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');
|
||||
|
||||
// Close modal on backdrop click
|
||||
modal.addEventListener('click', function(e) {
|
||||
if (e.target === this) closeModal();
|
||||
});
|
||||
document.getElementById('uploadModalClose').addEventListener('click', closeModal);
|
||||
|
||||
function closeModal() {
|
||||
if (submitBtn.disabled) return; // prevent close during upload
|
||||
modal.classList.remove('is-open');
|
||||
}
|
||||
|
||||
// Show selected file count
|
||||
filesInput.addEventListener('change', function() {
|
||||
var n = this.files.length;
|
||||
fileCount.textContent = n > 0 ? ('Выбрано файлов: ' + n) : '';
|
||||
});
|
||||
|
||||
// Submit via XHR for progress tracking
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
if (!filesInput.files.length) return;
|
||||
|
||||
var data = new FormData(form);
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
// Show progress bar, disable submit
|
||||
progress.style.display = 'block';
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Загрузка...';
|
||||
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 + '%';
|
||||
if (pct === 100) statusText.textContent = 'Обработка...';
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', function() {
|
||||
if (xhr.status >= 200 && xhr.status < 400) {
|
||||
bar.style.width = '100%';
|
||||
percent.textContent = '100%';
|
||||
statusText.textContent = 'Готово!';
|
||||
// Reload page to show uploaded media
|
||||
setTimeout(function() { window.location.reload(); }, 300);
|
||||
} else {
|
||||
statusText.textContent = 'Ошибка загрузки (' + xhr.status + ')';
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '{{ t.media_upload }}';
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener('error', function() {
|
||||
statusText.textContent = 'Ошибка соединения';
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '{{ t.media_upload }}';
|
||||
});
|
||||
|
||||
xhr.open('POST', form.action);
|
||||
xhr.send(data);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user