feat: complete production backend integration

This commit is contained in:
Marek Miklewicz
2026-06-02 20:51:57 +02:00
parent d3f2fd69db
commit 4b531a4c24
20 changed files with 853 additions and 40 deletions
+87 -1
View File
@@ -70,6 +70,20 @@ final class AppContext
}
}
public function syncActiveBackend(): void
{
if ($this->settings->backendMode() !== 'da') {
return;
}
$service = new DirectAdminVacationSyncService(
$this->rules,
new DirectAdminSyncRepository(),
new MailboxDirectory(),
new DirectAdminVacationApi()
);
$service->syncUser($this->daUser->username());
}
public function render(string $title, string $body, array $ok = [], array $errors = []): void
{
header('Content-Type: text/html; charset=UTF-8');
@@ -109,17 +123,89 @@ final class AppContext
private function scripts(): string
{
$selectedLabel = json_encode($this->t('Selected'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
$monthNames = json_encode($this->lang->code() === 'pl'
? ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień']
: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
return <<<JS
(function () {
var selectedLabel = {$selectedLabel};
document.querySelectorAll('[data-date-value]').forEach(function (btn) {
var monthNames = {$monthNames};
function pad(value) { return value < 10 ? '0' + value : String(value); }
function updateCanonical(name) {
var date = document.getElementById(name + '_date');
var canonical = document.getElementById(name);
if (!date || !canonical) { return; }
var time = document.querySelector('[data-time-for="' + name + '"]');
var period = document.querySelector('[data-period-for="' + name + '"]');
if (time) {
canonical.value = date.value + 'T' + (time.value || '09:00');
} else if (period) {
canonical.value = date.value + '|' + (period.value || 'morning');
}
}
function bindDateButton(btn) {
btn.addEventListener('click', function () {
var target = document.getElementById(btn.getAttribute('data-date-target'));
var label = document.getElementById(btn.getAttribute('data-date-label'));
if (target) { target.value = btn.getAttribute('data-date-value') || ''; }
if (label) { label.textContent = selectedLabel + ': ' + (btn.getAttribute('data-date-value') || ''); }
var calendar = btn.closest('[data-calendar-picker]');
if (calendar) { calendar.setAttribute('data-selected-date', btn.getAttribute('data-date-value') || ''); }
btn.closest('.br-restore-calendar').querySelectorAll('.br-cal-day').forEach(function (d) { d.classList.remove('is-selected'); });
btn.classList.add('is-selected');
if (calendar) { updateCanonical(calendar.getAttribute('data-calendar-name')); }
});
}
function renderMonth(calendar, year, month) {
var selected = calendar.getAttribute('data-selected-date') || '';
var tbody = calendar.querySelector('[data-calendar-days]');
var label = calendar.querySelector('[data-calendar-month-label]');
if (!tbody || !label) { return; }
var first = new Date(Date.UTC(year, month - 1, 1));
var offset = (first.getUTCDay() + 6) % 7;
var days = new Date(Date.UTC(year, month, 0)).getUTCDate();
var day = 1;
var html = '';
for (var week = 0; week < 6; week++) {
html += '<tr>';
for (var i = 0; i < 7; i++) {
if ((week === 0 && i < offset) || day > days) {
html += '<td class="br-cal-empty"></td>';
continue;
}
var value = year + '-' + pad(month) + '-' + pad(day);
var cls = value === selected ? ' is-selected' : '';
html += '<td><button type="button" class="br-cal-day' + cls + '" data-date-target="' + calendar.getAttribute('data-calendar-name') + '_date" data-date-label="' + calendar.getAttribute('data-calendar-name') + '_label" data-date-value="' + value + '">' + day + '</button></td>';
day++;
}
html += '</tr>';
if (day > days) { break; }
}
tbody.innerHTML = html;
label.textContent = monthNames[month - 1] + ' ' + year;
calendar.setAttribute('data-visible-month', year + '-' + pad(month));
tbody.querySelectorAll('[data-date-value]').forEach(bindDateButton);
}
document.querySelectorAll('[data-date-value]').forEach(function (btn) {
bindDateButton(btn);
});
document.querySelectorAll('[data-calendar-picker]').forEach(function (calendar) {
var name = calendar.getAttribute('data-calendar-name');
calendar.querySelectorAll('[data-calendar-prev],[data-calendar-next]').forEach(function (btn) {
btn.addEventListener('click', function () {
var parts = (calendar.getAttribute('data-visible-month') || '').split('-');
var year = parseInt(parts[0], 10);
var month = parseInt(parts[1], 10);
if (!year || !month) { return; }
month += btn.hasAttribute('data-calendar-next') ? 1 : -1;
if (month < 1) { month = 12; year--; }
if (month > 12) { month = 1; year++; }
renderMonth(calendar, year, month);
});
});
document.querySelectorAll('[data-time-for="' + name + '"],[data-period-for="' + name + '"]').forEach(function (control) {
control.addEventListener('change', function () { updateCanonical(name); });
control.addEventListener('input', function () { updateCanonical(name); });
});
});
})();