Files
web-petting/templates/admin/schedule.html
T
Ultradesu ff32e6bbaf
Build and Publish / Build and Publish Docker Image (push) Successful in 1m12s
init
2026-04-29 17:49:07 +03:00

127 lines
6.1 KiB
HTML

{% extends "admin/layout.html" %}
{% let active_page = "schedule" %}
{% block title %}{{ t.schedule_title }}{% endblock %}
{% block content %}
<div class="page-head">
<h1>{{ t.schedule_title }}</h1>
<a href="/admin/schedule/new?lang={{ lang.code() }}" class="button is-primary is-small">+ {{ t.schedule_new }}</a>
</div>
<div class="form-card" id="calendar-wrap" style="padding:0.5rem;">
<div id="calendar"></div>
</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.17/index.global.min.css">
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.17/index.global.min.js"></script>
{% if lang == Lang::Ru %}
<script src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.17/locales/ru.global.min.js"></script>
{% endif %}
<style>
#calendar-wrap { overflow: hidden; }
.fc { font-size: 0.85rem; }
.fc .fc-toolbar { flex-wrap: wrap; gap: 0.3rem; }
.fc .fc-toolbar-title { font-size: 1.1rem !important; }
.fc .fc-button { padding: 0.25rem 0.5rem !important; font-size: 0.8rem !important; }
.fc-event { cursor: pointer; border: none !important; padding: 2px 5px; border-radius: 4px; }
.fc .fc-day-today { background: #eef2ff !important; }
.fc .fc-day.day-weekend { background: #faf5f0; }
.fc .fc-day-today.day-weekend { background: #eef2ff !important; }
@media (max-width: 768px) {
.fc .fc-toolbar { font-size: 0.75rem; }
.fc .fc-toolbar-title { font-size: 0.95rem !important; }
.fc .fc-button { padding: 0.2rem 0.35rem !important; font-size: 0.72rem !important; }
}
.visit-modal-bg { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:100; align-items:center; justify-content:center; }
.visit-modal-bg.is-open { display:flex; }
.visit-modal { background:#fff; border-radius:12px; padding:1.5rem; width:90%; max-width:380px; box-shadow:0 4px 24px rgba(0,0,0,0.15); }
.visit-modal h3 { margin:0 0 0.75rem; font-size:1.1rem; }
.visit-modal .meta { color:#888; font-size:0.85rem; margin-bottom:0.75rem; line-height:1.6; }
.visit-modal .actions { display:flex; gap:0.5rem; flex-wrap:wrap; }
.visit-modal .actions form { margin:0; }
.color-dot { display:inline-block; width:12px; height:12px; border-radius:50%; margin-right:6px; vertical-align:middle; }
</style>
<div class="visit-modal-bg" id="visitModal">
<div class="visit-modal">
<h3><span class="color-dot" id="vmDot"></span><span id="vmTitle"></span></h3>
<div class="meta">
<div id="vmClient"></div>
<div id="vmAddress"></div>
<div id="vmAdmin"></div>
<div id="vmTime"></div>
<div id="vmNotes" style="margin-top:0.3rem;"></div>
</div>
<div id="vmStatus" style="margin-bottom:0.75rem;"></div>
<div class="actions" id="vmActions"></div>
<div style="display:flex;gap:0.5rem;margin-top:0.5rem;">
<a id="vmEditLink" href="#" class="button is-info is-small" style="flex:1;">{{ t.schedule_edit_title }}</a>
<button class="button is-light is-small" style="flex:1;" onclick="closeModal()">OK</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const lang = '{{ lang.code() }}';
const calEl = document.getElementById('calendar');
const statusLabels = {
scheduled: '{{ t.visit_status_scheduled }}',
completed: '{{ t.visit_status_completed }}',
cancelled: '{{ t.visit_status_cancelled }}'
};
const calendar = new FullCalendar.Calendar(calEl, {
locale: lang,
initialView: window.innerWidth < 768 ? 'listWeek' : 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
},
events: '/admin/schedule/events',
eventClick: function(info) {
info.jsEvent.preventDefault();
const ev = info.event;
const p = ev.extendedProps;
document.getElementById('vmDot').style.background = p.client_color || '#7c6ed4';
document.getElementById('vmTitle').textContent = p.client_name;
document.getElementById('vmClient').innerHTML = p.client_phone ? ('<a href="tel:' + p.client_phone + '" style="color:inherit;text-decoration:none;">📞 ' + p.client_phone + '</a>') : '';
document.getElementById('vmAddress').textContent = p.client_address ? ('📍 ' + p.client_address) : '';
document.getElementById('vmAdmin').textContent = '👤 ' + p.admin_name;
document.getElementById('vmTime').textContent = '🕐 ' + p.time_start + ' — ' + p.time_end;
document.getElementById('vmNotes').textContent = p.notes || '';
const badge = '<span class="badge badge-visit-' + p.status + '">' + statusLabels[p.status] + '</span>';
document.getElementById('vmStatus').innerHTML = badge;
let actions = '';
if (p.status === 'scheduled') {
actions += '<form method="post" action="/admin/schedule/' + ev.id + '/done"><button class="button is-small is-success is-outlined">{{ t.schedule_mark_done }}</button></form>';
actions += '<form method="post" action="/admin/schedule/' + ev.id + '/cancel"><button class="button is-small is-danger is-outlined">{{ t.schedule_cancel }}</button></form>';
}
document.getElementById('vmActions').innerHTML = actions;
document.getElementById('vmEditLink').href = '/admin/schedule/' + ev.id + '/edit?lang=' + lang;
document.getElementById('visitModal').classList.add('is-open');
},
dayCellClassNames: function(arg) {
var dow = arg.date.getDay();
if (dow === 0 || dow === 6) return ['day-weekend'];
return [];
},
height: 'auto',
navLinks: true,
nowIndicator: true,
});
calendar.render();
document.getElementById('visitModal').addEventListener('click', function(e) {
if (e.target === this) closeModal();
});
});
function closeModal() {
document.getElementById('visitModal').classList.remove('is-open');
}
</script>
{% endblock %}