Globinours/app/Views/agenda.php

561 lines
32 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
$first = $cursor->modify('first day of this month');
$offset = (int)$first->format('N') - 1;
$days = (int)$cursor->format('t');
$trailing = (7 - (($offset + $days) % 7)) % 7;
$weekStart = $weekCursor->modify('monday this week');
$weekEnd = $weekStart->modify('+6 days');
$byDay = [];
foreach ($items as $item) {
$byDay[substr($item['instance_starts_at'], 0, 10)][] = $item;
}$edit = $editItem ?? [];
$editStart = !empty($edit['starts_at']) ? new DateTimeImmutable($edit['starts_at']) : null;
$colors = ['appointment' => 'blue','veterinary' => 'red','transport' => 'orange','pre_adoption' => 'purple','task' => 'green','other' => 'secondary','vaccine' => 'cyan','deworming' => 'yellow','treatment_start' => 'teal','treatment_end' => 'pink'];
$sourceTabs = ['vaccine' => 'tab-vac','deworming' => 'tab-med','treatment_start' => 'tab-trt','treatment_end' => 'tab-trt'];
$legend = ['organization' => ['appointment' => '#206bc4','veterinary' => '#d63939','transport' => '#f76707','pre_adoption' => '#ae3ec9','task' => '#2fb344'],'medical' => ['vaccine' => '#17a2b8','deworming' => '#f59f00','treatment_start' => '#0ca678','treatment_end' => '#d6336c']];
?>
<style>
.agenda-calendar {
display: grid;
grid-template-columns: repeat(7, minmax(120px, 1fr));
min-width: 735px;
border-radius: 12px;
overflow: hidden
}
.agenda-weekday {
padding: .65rem;
background: var(--tblr-bg-surface-secondary);
font-weight: 700;
text-align: center
}
.agenda-day {
min-height: 128px;
padding: .5rem;
border-top: 1px solid var(--tblr-border-color);
border-right: 1px solid var(--tblr-border-color);
background: var(--tblr-bg-surface)
}
.agenda-day:nth-child(7n) {
border-right: 0
}
.agenda-day.muted {
background: rgba(98, 105, 118, .04)
}
.agenda-number {
font-weight: 700;
font-size: .8rem
}
.agenda-event {
display: block;
margin-top: .3rem;
padding: .3rem .4rem;
border-radius: 6px;
background: rgba(32, 107, 196, .09);
border-left: 3px solid var(--tblr-primary);
font-size: .68rem;
color: inherit;
text-decoration: none
}
.agenda-event strong {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis
}
.agenda-event.red {
border-color: #d63939;
background: rgba(214, 57, 57, .09)
}
.agenda-event.orange {
border-color: #f76707;
background: rgba(247, 103, 7, .09)
}
.agenda-event.purple {
border-color: #ae3ec9;
background: rgba(174, 62, 201, .09)
}
.agenda-event.green {
border-color: #2fb344;
background: rgba(47, 179, 68, .09)
}
.agenda-event.cyan {
border-color: #17a2b8;
background: rgba(23, 162, 184, .1)
}
.agenda-event.yellow {
border-color: #f59f00;
background: rgba(245, 159, 0, .11)
}
.agenda-event.teal {
border-color: #0ca678;
background: rgba(12, 166, 120, .1)
}
.agenda-event.pink {
border-color: #d6336c;
background: rgba(214, 51, 108, .1)
}
.agenda-legend {
display: flex;
flex-wrap: wrap;
gap: .65rem;
padding: .7rem;
background: rgba(98, 105, 118, .045);
border-radius: 10px
}
.agenda-legend-group {
display: flex;
flex: 1 1 360px;
align-items: center;
flex-wrap: wrap;
gap: .45rem
}
.agenda-legend-title {
display: inline-flex;
align-items: center;
gap: .35rem;
margin-right: .15rem;
font-size: .72rem;
font-weight: 700;
color: var(--tblr-secondary);
text-transform: uppercase;
letter-spacing: .025em
}
.agenda-legend-item {
display: inline-flex;
align-items: center;
gap: .38rem;
padding: .28rem .55rem;
border-radius: 999px;
background: var(--tblr-bg-surface);
box-shadow: 0 1px 2px rgba(24, 36, 51, .08);
font-size: .72rem;
color: var(--tblr-body-color);
white-space: nowrap
}
.agenda-legend-dot {
width: .55rem;
height: .55rem;
border-radius: 50%;
flex: 0 0 auto;
box-shadow: 0 0 0 2px rgba(255, 255, 255, .8)
}
@media(max-width:900px) {
.agenda-calendar {
grid-template-columns: repeat(7, minmax(105px, 1fr))
}
.agenda-legend-group {
flex-basis: 100%
}
}
</style>
<style>
.agenda-calendar-week .agenda-day {
min-height: 360px
}
.agenda-day.agenda-day-clickable {
position: relative;
cursor: pointer;
transition: background-color .15s ease
}
.agenda-day.agenda-day-clickable:hover {
background-color: rgba(32, 107, 196, .055)
}
.agenda-day.agenda-day-clickable::after {
content: '+';
position: absolute;
right: .45rem;
top: .35rem;
width: 1.35rem;
height: 1.35rem;
display: grid;
place-items: center;
border-radius: 50%;
color: var(--tblr-primary);
background: rgba(32, 107, 196, .1);
font-weight: 700;
opacity: 0;
transform: scale(.85);
transition: .15s ease
}
.agenda-day.agenda-day-clickable:hover::after,
.agenda-day.agenda-day-clickable:focus-visible::after {
opacity: 1;
transform: scale(1)
}
.agenda-day.agenda-day-clickable:focus-visible {
outline: 2px solid var(--tblr-primary);
outline-offset: -2px
}
</style>
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
<div class="text-muted"><?= h(t('agenda.intro')) ?></div>
<?php if (PermissionService::can('agenda', 'edit')): ?><button class="btn btn-primary" id="agendaAddButton"
data-bs-toggle="modal" data-bs-target="#agendaModal"> <?= h(t('agenda.add')) ?></button><?php endif; ?>
</div>
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3">
<div class="btn-group" role="group" aria-label="<?= h(t('agenda.view')) ?>"><a
class="btn btn-sm <?= $viewMode === 'month' ? 'btn-primary' : 'btn-outline-secondary' ?>"
href="/agenda?view=month&amp;month=<?= h(date('Y-m')) ?>">▦ <?= h(t('agenda.month_view')) ?></a><a
class="btn btn-sm <?= $viewMode === 'week' ? 'btn-primary' : 'btn-outline-secondary' ?>"
href="/agenda?view=week&amp;date=<?= h(date('Y-m-d')) ?>">▥ <?= h(t('agenda.week_view')) ?></a></div>
<div class="d-flex flex-wrap gap-2"><a class="btn btn-sm btn-outline-secondary"
href="/agenda?view=<?= h($viewMode) ?><?= $viewMode === 'week' ? '&amp;date=' . h(date('Y-m-d')) : '&amp;month=' . h(date('Y-m')) ?>"><?= h(t('agenda.today')) ?></a><a
class="btn btn-sm btn-outline-primary"
href="/agenda/export.ics?view=<?= h($viewMode) ?><?= $viewMode === 'week' ? '&amp;date=' . h($weekCursor->format('Y-m-d')) : '&amp;month=' . h($cursor->format('Y-m')) ?>">⇩
<?= h(t('agenda.export_ics')) ?></a></div>
</div>
<form class="card card-body mb-3 py-2" method="get" action="/agenda" id="agendaFilters"><input type="hidden" name="view"
value="<?= h($viewMode) ?>"><?php if ($viewMode === 'week'): ?><input type="hidden" name="date"
value="<?= h($weekCursor->format('Y-m-d')) ?>"><?php else: ?><input type="hidden" name="month"
value="<?= h($cursor->format('Y-m')) ?>"><?php endif; ?><div class="row g-2 align-items-end">
<div class="col-sm-6 col-lg-2"><label class="form-label small mb-1"><?= h(t('agenda.scope')) ?></label><select
class="form-select form-select-sm" name="scope" onchange="this.form.submit()">
<option value="all"><?= h(t('agenda.scope_all')) ?></option>
<option value="mine" <?= $filters['scope'] === 'mine' ? 'selected' : '' ?>>
<?= h(t('agenda.scope_mine')) ?></option>
</select></div>
<div class="col-sm-6 col-lg-2"><label
class="form-label small mb-1"><?= h(t('agenda.assigned')) ?></label><select
class="form-select form-select-sm" name="assigned_user_id" onchange="this.form.submit()">
<option value="">—</option><?php foreach ($users as $u): ?><option value="<?= (int)$u['id'] ?>"
<?= $filters['assigned_user_id'] == (int)$u['id'] ? 'selected' : '' ?>><?= h($u['name']) ?></option>
<?php endforeach; ?>
</select></div>
<div class="col-sm-6 col-lg-2"><label
class="form-label small mb-1"><?= h(t('agenda.volunteer')) ?></label><select
class="form-select form-select-sm" name="volunteer_contact_id" onchange="this.form.submit()">
<option value="">—</option><?php foreach ($volunteers as $v): ?><option value="<?= (int)$v['id'] ?>"
<?= $filters['volunteer_contact_id'] == (int)$v['id'] ? 'selected' : '' ?>><?= h($v['name']) ?>
</option><?php endforeach; ?>
</select></div>
<div class="col-sm-6 col-lg-2"><label class="form-label small mb-1"><?= h(t('agenda.type')) ?></label><select
class="form-select form-select-sm" name="category" onchange="this.form.submit()">
<option value=""><?= h(t('common.all')) ?></option><?php foreach (array_keys($colors) as $type): ?>
<option value="<?= h($type) ?>" <?= $filters['category'] === $type ? 'selected' : '' ?>>
<?= h(t('agenda.type_' . $type)) ?></option><?php endforeach; ?>
</select></div>
<div class="col-sm-8 col-lg-3"><label class="form-label small mb-1"><?= h(t('common.search')) ?></label><input
class="form-control form-control-sm" name="q" value="<?= h($filters['q']) ?>"
placeholder="<?= h(t('agenda.search_placeholder')) ?>"></div>
<div class="col-sm-4 col-lg-1 d-flex gap-1"><button class="btn btn-sm btn-primary flex-fill"
title="<?= h(t('common.search')) ?>">⌕</button><a class="btn btn-sm btn-outline-secondary"
href="/agenda?view=<?= h($viewMode) ?>" title="<?= h(t('common.reset')) ?>">×</a></div>
<div class="col-12"><input type="hidden" name="show_medical" value="0"><label
class="form-check form-switch mb-0"><input class="form-check-input" type="checkbox" name="show_medical"
value="1" <?= $filters['show_medical'] ? 'checked' : '' ?> onchange="this.form.submit()"><span
class="form-check-label small"><?= h(t('agenda.show_medical')) ?></span></label></div>
</div>
</form>
<div class="agenda-legend mb-3" aria-label="<?= h(t('agenda.legend')) ?>">
<?php foreach ($legend as $group => $entries): ?><div class="agenda-legend-group"><span
class="agenda-legend-title"><?= $group === 'medical' ? '❤' : '📅' ?>
<?= h(t('agenda.legend_' . $group)) ?></span><?php foreach ($entries as $type => $color): ?><span
class="agenda-legend-item"><span class="agenda-legend-dot"
style="background:<?= h($color) ?>"></span><?= h(t('agenda.type_' . $type)) ?></span><?php endforeach; ?>
</div><?php endforeach; ?></div>
<details class="card mb-3">
<summary class="card-header cursor-pointer"><span class="card-title">🔄
<?= h(t('agenda.subscription')) ?></span><span
class="ms-auto badge <?= $feedEnabled ? 'bg-green-lt' : 'bg-secondary-lt' ?>"><?= h(t($feedEnabled ? 'agenda.feed_active' : 'agenda.feed_inactive')) ?></span>
</summary>
<div class="card-body">
<p class="text-muted"><?= h(t('agenda.subscription_help')) ?></p><?php if ($feedUrl): ?><div
class="alert alert-success"><strong><?= h(t('agenda.copy_now')) ?></strong>
<div class="input-group mt-2"><input class="form-control font-monospace" id="agendaFeedUrl" readonly
value="<?= h($feedUrl) ?>"><button class="btn btn-outline-success" type="button"
onclick="navigator.clipboard.writeText(document.getElementById('agendaFeedUrl').value)"><?= h(t('common.copy')) ?></button>
</div>
</div><?php elseif ($feedEnabled): ?><div class="alert alert-info"><?= h(t('agenda.feed_hidden')) ?></div>
<?php endif; ?><div class="d-flex flex-wrap gap-2">
<form method="post" action="/agenda/feed/regenerate"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>"><button
class="btn btn-outline-primary"><?= h($feedEnabled ? t('agenda.regenerate_feed') : t('agenda.create_feed')) ?></button>
</form><?php if ($feedEnabled): ?><form method="post" action="/agenda/feed/revoke"
onsubmit="return confirm(<?= h(json_encode(t('agenda.revoke_confirm'), JSON_HEX_APOS | JSON_HEX_QUOT)) ?>)">
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>"><button
class="btn btn-outline-danger"><?= h(t('agenda.revoke_feed')) ?></button></form><?php endif; ?>
</div>
</div>
</details>
<template id="agendaVolunteerField">
<div class="col-md-4 agenda-volunteer-field"><label
class="form-label"><?= h(t('agenda.volunteers')) ?></label><select class="form-select"
name="volunteer_contact_ids[]" multiple size="4"><?php $selectedVolunteers = array_map('intval', array_filter(explode(',', (string)($edit['volunteer_ids'] ?? ''))));
foreach ($volunteers as $v): ?><option value="<?= (int)$v['id'] ?>"
<?= in_array((int)$v['id'], $selectedVolunteers, true) ? 'selected' : '' ?>><?= h($v['name']) ?>
</option><?php endforeach; ?></select>
<div class="form-hint"><?= h(t('agenda.volunteers_help')) ?></div>
</div>
</template>
<script>
document.addEventListener('DOMContentLoaded', () => {
const assigned = document.querySelector('#agendaModal select[name="assigned_user_id"]');
const template = document.getElementById('agendaVolunteerField');
if (assigned && template && !document.querySelector('#agendaModal .agenda-volunteer-field')) assigned
.closest('[class*="col-md-"]')?.after(template.content.cloneNode(true));
const form = document.getElementById('agendaFilters');
if (form) {
const params = new URLSearchParams(new FormData(form));
['view', 'date', 'month'].forEach(key => params.delete(key));
document.querySelectorAll('a[href^="/agenda?"] , a[href^="/agenda/export.ics?"]').forEach(link => {
if (link.title === < ? = json_encode(t('common.reset')) ? > ) return;
const url = new URL(link.href, location.origin);
params.forEach((value, key) => url.searchParams.set(key, value));
link.href = url.pathname + '?' + url.searchParams.toString();
});
}
const modal = document.getElementById('agendaModal');
const addButton = document.getElementById('agendaAddButton');
if (modal && addButton && < ? = (!$edit && PermissionService::can('agenda', 'edit')) ? 'true' :
'false' ? > ) {
const month = < ? = json_encode($cursor - > format('Y-m')) ? > ;
const weekDates = < ? = json_encode(array_map(static fn($i) => $weekStart - > modify('+'.$i.
' days') - > format('Y-m-d'), range(0, 6))) ? > ;
document.querySelectorAll('.agenda-calendar .agenda-day:not(.muted)').forEach((cell, index) => {
const number = cell.querySelector('.agenda-number')?.textContent.trim();
const date = < ? = $viewMode === 'week' ? 'weekDates[index]' :
'month+\'-\'+String(parseInt(number,10)).padStart(2,\'0\')' ? > ;
if (!date) return;
cell.classList.add('agenda-day-clickable');
cell.tabIndex = 0;
cell.setAttribute('role', 'button');
cell.setAttribute('aria-label', < ? = json_encode(t('agenda.add_on_date')) ? > +' ' +
date.split('-').reverse().join('/'));
const open = event => {
if (event.target.closest('.agenda-event,button,a,input,select,textarea,label'))
return;
modal.querySelector('input[name="date"]').value = date;
addButton.click();
};
cell.addEventListener('click', open);
cell.addEventListener('keydown', event => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
open(event);
}
});
});
}
});
</script>
<?php if ($status): ?><div class="alert <?= $status === 'conflict' ? 'alert-warning' : 'alert-success' ?>">
<?= $status === 'conflict' ? '⚠️' : '✅' ?>
<?= h(t($status === 'conflict' ? 'agenda.conflict_saved' : 'agenda.saved')) ?></div>
<?php endif; ?><?php if ($error): ?><div class="alert alert-danger"><?= h($error) ?></div><?php endif; ?>
<?php if ($viewMode === 'month'): ?><div class="card mb-3">
<div class="card-header d-flex justify-content-between"><a class="btn btn-sm btn-outline-secondary"
href="/agenda?view=month&amp;month=<?= h($cursor->modify('-1 month')->format('Y-m')) ?>">←</a>
<h3 class="card-title text-capitalize">
<?= h(I18n::locale() === 'fr' ? ['janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre'][(int)$cursor->format('n') - 1] . ' ' . $cursor->format('Y') : $cursor->format('F Y')) ?>
</h3><a class="btn btn-sm btn-outline-secondary"
href="/agenda?view=month&amp;month=<?= h($cursor->modify('+1 month')->format('Y-m')) ?>">→</a>
</div>
<div class="card-body p-0 overflow-auto">
<div class="agenda-calendar">
<?php foreach ([t('agenda.mon'),t('agenda.tue'),t('agenda.wed'),t('agenda.thu'),t('agenda.fri'),t('agenda.sat'),t('agenda.sun')] as $label): ?>
<div class="agenda-weekday"><?= h($label) ?></div>
<?php endforeach; ?><?php for ($i = 0;$i < $offset;$i++): ?><div class="agenda-day muted"></div>
<?php endfor; ?><?php for ($day = 1;$day <= $days;$day++): $date = $cursor->format('Y-m-') . str_pad((string)$day, 2, '0', STR_PAD_LEFT); ?>
<div class="agenda-day <?= $date === date('Y-m-d') ? 'bg-yellow-lt' : '' ?>">
<div class="agenda-number"><?= $day ?></div>
<?php foreach ($byDay[$date] ?? [] as $item): $source = $item['source_type'] ?? 'agenda';
$isLinkedReminder = $source !== 'agenda';
$eventHref = $isLinkedReminder ? '/animal?id=' . (int)$item['animal_id'] . '#' . ($sourceTabs[$source] ?? 'tab-med') : '/agenda?view=month&amp;month=' . h($cursor->format('Y-m')) . '&amp;edit=' . (int)$item['id']; ?><a
class="agenda-event <?= h($colors[$item['item_type']] ?? 'secondary') ?>" href="<?= $eventHref ?>"
title="<?= h($item['title']) ?>"><strong><?= $item['all_day'] ? '' : h(substr($item['instance_starts_at'], 11, 5) . ' ') ?><?= h($item['title']) ?></strong><?php if ($item['animal_name']): ?>🐾
<?= h($item['animal_name']) ?><?php endif; ?></a><?php endforeach; ?>
</div><?php endfor; ?><?php for ($i = 0;$i < $trailing;$i++): ?><div class="agenda-day muted"></div>
<?php endfor; ?></div>
</div>
</div><?php else: ?><div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center"><a
class="btn btn-sm btn-outline-secondary"
href="/agenda?view=week&amp;date=<?= h($weekStart->modify('-7 days')->format('Y-m-d')) ?>">←</a>
<h3 class="card-title">
<?= h(t('agenda.week_range', ['start' => $weekStart->format('d/m/Y'),'end' => $weekEnd->format('d/m/Y')])) ?>
</h3><a class="btn btn-sm btn-outline-secondary"
href="/agenda?view=week&amp;date=<?= h($weekStart->modify('+7 days')->format('Y-m-d')) ?>">→</a>
</div>
<div class="card-body p-0 overflow-auto">
<div class="agenda-calendar agenda-calendar-week"><?php $weekLabels = [t('agenda.mon'),t('agenda.tue'),t('agenda.wed'),t('agenda.thu'),t('agenda.fri'),t('agenda.sat'),t('agenda.sun')];
foreach ($weekLabels as $i => $label):$weekDate = $weekStart->modify('+' . $i . ' days'); ?><div
class="agenda-weekday"><span class="d-block"><?= h($label) ?></span><span
class="small fw-normal"><?= h($weekDate->format('d/m')) ?></span></div><?php endforeach; ?><?php for ($i = 0;$i < 7;$i++):$weekDate = $weekStart->modify('+' . $i . ' days');
$date = $weekDate->format('Y-m-d'); ?><div
class="agenda-day <?= $date === date('Y-m-d') ? 'bg-yellow-lt' : '' ?>">
<div class="agenda-number"><?= h($weekDate->format('d/m')) ?></div>
<?php foreach ($byDay[$date] ?? [] as $item):$source = $item['source_type'] ?? 'agenda';
$isLinkedReminder = $source !== 'agenda';
$eventHref = $isLinkedReminder ? '/animal?id=' . (int)$item['animal_id'] . '#' . ($sourceTabs[$source] ?? 'tab-med') : '/agenda?view=week&amp;date=' . h($weekStart->format('Y-m-d')) . '&amp;edit=' . (int)$item['id']; ?><a
class="agenda-event <?= h($colors[$item['item_type']] ?? 'secondary') ?>" href="<?= $eventHref ?>"
title="<?= h($item['title']) ?>"><strong><?= $item['all_day'] ? '' : h(substr($item['instance_starts_at'], 11, 5) . ' ') ?><?= h($item['title']) ?></strong><?php if ($item['animal_name']): ?>🐾
<?= h($item['animal_name']) ?><?php endif; ?></a><?php endforeach; ?>
</div><?php endfor; ?></div>
</div>
</div><?php endif; ?>
<div class="card">
<div class="card-header">
<h3 class="card-title"><?= h(t('agenda.upcoming')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('common.date')) ?></th>
<th><?= h(t('agenda.event')) ?></th>
<th><?= h(t('animal.animal')) ?></th>
<th><?= h(t('agenda.assigned')) ?></th>
<th><?= h(t('agenda.status')) ?></th>
<th></th>
</tr>
</thead>
<tbody><?php foreach ($upcoming as $item): $source = $item['source_type'] ?? 'agenda';
$isLinkedReminder = $source !== 'agenda';
$animalHref = '/animal?id=' . (int)$item['animal_id'] . '#' . ($sourceTabs[$source] ?? 'tab-med'); ?><tr>
<td><?= h(date('d/m/Y', strtotime($item['instance_starts_at']))) ?><div class="small text-muted">
<?= $item['all_day'] ? h(t('agenda.all_day')) : h(substr($item['instance_starts_at'], 11, 5)) ?>
</div>
</td>
<td><strong><?= h($item['title']) ?></strong>
<div class="small text-muted">
<?= h(t('agenda.type_' . $item['item_type'])) ?><?= $item['location'] ? ' · ' . h($item['location']) : '' ?>
</div>
</td>
<td><?= $item['animal_name'] ? '<a href="' . h($animalHref) . '">' . h($item['animal_name']) . '</a>' : '—' ?>
</td>
<td><?= h($item['assigned_name'] ?: '—') ?></td>
<td><span
class="badge bg-<?= h($item['status'] === 'completed' ? 'green' : 'blue') ?>-lt"><?= h(t('agenda.status_' . $item['status'])) ?></span>
</td>
<td class="text-end"><?php if ($isLinkedReminder): ?><a class="btn btn-sm btn-outline-secondary"
href="<?= h($animalHref) ?>"><?= h(t('agenda.open_animal')) ?></a><?php elseif (PermissionService::can('agenda', 'edit')): ?>
<div class="d-flex gap-1 justify-content-end"><a class="btn btn-sm btn-outline-secondary"
href="/agenda?edit=<?= (int)$item['id'] ?>"><?= h(t('common.edit')) ?></a><?php if ($item['status'] === 'planned'): ?>
<form method="post" action="/agenda/status"><input type="hidden" name="id"
value="<?= (int)$item['id'] ?>"><input type="hidden" name="status"
value="completed"><button class="btn btn-sm btn-outline-success">✓</button></form>
<?php endif; ?></div><?php endif; ?></td>
</tr><?php endforeach; ?><?php if (!$upcoming): ?><tr>
<td colspan="6" class="text-center text-muted py-4"><?= h(t('agenda.none')) ?></td>
</tr><?php endif; ?></tbody>
</table>
</div>
</div>
<?php if (PermissionService::can('agenda', 'edit')): ?><div class="modal modal-blur fade <?= $edit ? 'show' : '' ?>"
id="agendaModal" tabindex="-1"
<?= $edit ? 'style="display:block" aria-modal="true" role="dialog"' : 'aria-hidden="true"' ?>>
<div class="modal-dialog modal-lg modal-dialog-centered">
<form class="modal-content" method="post" action="/agenda/save"><input type="hidden" name="id"
value="<?= (int)($edit['id'] ?? 0) ?>">
<div class="modal-header">
<h5 class="modal-title"><?= h($edit ? t('agenda.edit') : t('agenda.add')) ?></h5><a class="btn-close"
href="/agenda?month=<?= h($cursor->format('Y-m')) ?>" aria-label="<?= h(t('common.close')) ?>"></a>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-4"><label class="form-label"><?= h(t('agenda.type')) ?></label><select
class="form-select" name="item_type"><?php foreach (AgendaService::TYPES as $type): ?>
<option value="<?= h($type) ?>"
<?= ($edit['item_type'] ?? 'task') === $type ? 'selected' : '' ?>>
<?= h(t('agenda.type_' . $type)) ?></option><?php endforeach; ?></select></div>
<div class="col-md-8"><label class="form-label"><?= h(t('common.title')) ?></label><input
class="form-control" name="title" required maxlength="180"
value="<?= h($edit['title'] ?? '') ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('common.date')) ?></label><input
class="form-control" type="date" name="date" required
value="<?= h($editStart ? $editStart->format('Y-m-d') : date('Y-m-d')) ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('agenda.time')) ?></label><input
class="form-control" type="time" name="time"
value="<?= h($editStart ? $editStart->format('H:i') : '09:00') ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('agenda.end_date')) ?></label><input
class="form-control" type="date" name="end_date"
value="<?= h(!empty($edit['ends_at']) ? substr($edit['ends_at'], 0, 10) : '') ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('agenda.end_time')) ?></label><input
class="form-control" type="time" name="end_time"
value="<?= h(!empty($edit['ends_at']) ? substr($edit['ends_at'], 11, 5) : '') ?>"></div>
<div class="col-12"><label class="form-check"><input class="form-check-input" type="checkbox"
name="all_day" <?= !empty($edit['all_day']) ? 'checked' : '' ?>><span
class="form-check-label"><?= h(t('agenda.all_day')) ?></span></label></div>
<div class="col-md-4"><label class="form-label"><?= h(t('agenda.priority')) ?></label><select
class="form-select"
name="priority"><?php foreach (['low','normal','high','urgent'] as $p): ?><option
value="<?= $p ?>" <?= ($edit['priority'] ?? 'normal') === $p ? 'selected' : '' ?>>
<?= h(t('agenda.priority_' . $p)) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label"><?= h(t('agenda.assigned')) ?></label><select
class="form-select" name="assigned_user_id">
<option value="">—</option><?php foreach ($users as $u): ?><option
value="<?= (int)$u['id'] ?>"
<?= (int)($edit['assigned_user_id'] ?? 0) === (int)$u['id'] ? 'selected' : '' ?>>
<?= h($u['name']) ?></option><?php endforeach; ?>
</select></div>
<div class="col-md-4"><label class="form-label"><?= h(t('agenda.reminder')) ?></label><select
class="form-select" name="reminder_minutes">
<option value="">—</option><?php foreach ([0,30,60,1440,2880,10080] as $m): ?><option
value="<?= $m ?>"
<?= ($edit['reminder_minutes'] ?? '') !== '' && (int)$edit['reminder_minutes'] === $m ? 'selected' : '' ?>>
<?= h(t('agenda.reminder_' . $m)) ?></option><?php endforeach; ?>
</select></div>
<div class="col-md-6"><label class="form-label"><?= h(t('animal.animal')) ?></label><select
class="form-select" name="animal_id">
<option value="">—</option><?php foreach ($animals as $a): ?><option
value="<?= (int)$a['id'] ?>"
<?= (int)($edit['animal_id'] ?? 0) === (int)$a['id'] ? 'selected' : '' ?>>
<?= h($a['name']) ?></option><?php endforeach; ?>
</select></div>
<div class="col-md-6"><label class="form-label"><?= h(t('agenda.contact')) ?></label><select
class="form-select" name="contact_id">
<option value="">—</option><?php foreach ($contacts as $c): ?><option
value="<?= (int)$c['id'] ?>"
<?= (int)($edit['contact_id'] ?? 0) === (int)$c['id'] ? 'selected' : '' ?>>
<?= h($c['name']) ?></option><?php endforeach; ?>
</select></div>
<div class="col-md-6"><label class="form-label"><?= h(t('agenda.location')) ?></label><input
class="form-control" name="location" value="<?= h($edit['location'] ?? '') ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('agenda.repeat')) ?></label><select
class="form-select"
name="repeat_rule"><?php foreach (['none','daily','weekly','monthly'] as $r): ?><option
value="<?= $r ?>" <?= ($edit['repeat_rule'] ?? 'none') === $r ? 'selected' : '' ?>>
<?= h(t('agenda.repeat_' . $r)) ?></option><?php endforeach; ?></select></div>
<div class="col-md-3"><label class="form-label"><?= h(t('agenda.repeat_until')) ?></label><input
class="form-control" type="date" name="repeat_until"
value="<?= h($edit['repeat_until'] ?? '') ?>"></div>
<div class="col-12"><label class="form-label"><?= h(t('common.description')) ?></label><textarea
class="form-control" name="description"
rows="3"><?= h($edit['description'] ?? '') ?></textarea></div>
</div>
</div>
<div class="modal-footer"><a class="btn me-auto" href="/agenda"><?= h(t('common.cancel')) ?></a><button
class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
</form>
</div>
</div><?php if ($edit): ?><div class="modal-backdrop fade show"></div><?php endif; ?><?php endif; ?>