Globinours/app/Views/admin_grants.php

314 lines
21 KiB
PHP
Raw Permalink 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 $money = static fn ($v) => number_format((float)$v, 2, ',', ' ') . ' €';
$date = static fn ($v) => $v ? (new DateTimeImmutable($v))->format('d/m/Y') : '—'; ?>
<?php if ($saved): ?><div class="alert alert-success"><?= h(t('grant.saved')) ?></div><?php endif; ?>
<?php if ($error): ?><div class="alert alert-danger"><?= h(t('grant.save_error')) ?></div><?php endif; ?>
<div class="card mb-4">
<div class="card-body d-flex flex-wrap align-items-end gap-3">
<form method="get" action="/admin/grants" class="d-flex align-items-end gap-2">
<div><label class="form-label"><?= h(t('grant.file_year')) ?></label><select class="form-select"
name="year"><?php for ($y = (int)date('Y') + 1;$y >= (int)date('Y') - 10;$y--): ?><option
value="<?= $y ?>" <?= $y === $year ? 'selected' : '' ?>><?= $y ?></option>
<?php endfor; ?></select></div><button class="btn btn-primary"><?= h(t('common.show')) ?></button>
</form>
<div class="ms-auto d-flex gap-2"><a class="btn btn-outline-secondary"
href="/admin/register?year=<?= $year ?>"><?= h(t('grant.register', ['year' => $year])) ?></a><a
class="btn btn-dark" target="_blank" href="/admin/grants/print?year=<?= $year ?>">🖨
<?= h(t('grant.print_report')) ?></a></div>
</div>
</div>
<div class="row row-cards mb-4">
<?php foreach ([[t('grant.entries'),$stats['entries'],'green'],[t('grant.exits'),$stats['exits'],'red'],[t('grant.adoptions'),$stats['adoptions'],'blue'],[t('grant.deaths'),$stats['deaths'],'dark'],[t('grant.births'),$stats['births'],'purple'],[t('grant.vaccinations'),$stats['vaccinations'],'cyan'],[t('grant.treatments'),$stats['treatments'],'orange'],[t('grant.current'),$stats['current'],'azure']] as [$label,$value,$color]): ?>
<div class="col-6 col-md-3">
<div class="card h-100">
<div class="card-status-top bg-<?= $color ?>"></div>
<div class="card-body">
<div class="text-muted small text-uppercase"><?= h($label) ?></div>
<div class="display-6 fw-bold"><?= (int)$value ?></div>
</div>
</div>
</div><?php endforeach; ?>
</div>
<div class="alert alert-info"><?= h(t('grant.indicators_help', ['year' => $year])) ?> <?= h(t('grant.avg_adoption')) ?>
: <strong><?= $stats['avg'] > 0 ? h(t('common.days', ['count' => round($stats['avg'])])) : '—' ?></strong>.</div>
<div class="card mb-4">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('grant.comparison', ['year' => $year - 1])) ?></h3>
<div class="text-muted small"><?= h(t('grant.comparison_help')) ?></div>
</div>
</div>
<div class="table-responsive">
<table class="table table-vcenter mb-0">
<thead>
<tr>
<th><?= h(t('grant.indicator')) ?></th>
<th class="text-end"><?= $year - 1 ?></th>
<th class="text-end"><?= $year ?></th>
<th class="text-end"><?= h(t('grant.evolution')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach (['entries' => t('grant.entries'),'exits' => t('grant.exits'),'adoptions' => t('grant.adoptions'),'deaths' => t('grant.deaths'),'births' => t('grant.births'),'vaccinations' => t('grant.vaccinations'),'treatments' => t('grant.treatments'),'avg' => t('grant.avg_adoption')] as $key => $label): $c = $comparison[$key]; ?>
<tr>
<td><?= h($label) ?></td>
<td class="text-end"><?= (int)round($c['previous']) ?></td>
<td class="text-end fw-bold"><?= (int)round($c['current']) ?></td>
<td class="text-end"><span
class="badge <?= $c['difference'] > 0 ? 'bg-blue-lt' : ($c['difference'] < 0 ? 'bg-orange-lt' : 'bg-secondary-lt') ?>"><?= $c['difference'] > 0 ? '+' : '' ?><?= (int)round($c['difference']) ?><?= $c['percent'] !== null ? ' · ' . ($c['percent'] > 0 ? '+' : '') . number_format($c['percent'], 1, ',', ' ') . ' %' : '' ?></span>
</td>
</tr><?php endforeach; ?></tbody>
</table>
</div>
</div>
<?php
$detailGroups = [
[t('grant.sex_entries'),$details['sex'],[]],
[t('grant.age_entry'),$details['ages'],[]],
[t('grant.origins'),$details['origins'],[]],
[t('grant.death_causes'),$details['deathCauses'],array_combine(array_keys(DeathController::CAUSES), array_map(static fn ($key) => DeathController::causeLabel($key), array_keys(DeathController::CAUSES)))],
[t('grant.adoption_stay'),$details['stays'],[]],
];
?>
<div class="row row-cards mb-4">
<?php foreach ($detailGroups as [$groupTitle,$items,$labels]): $maximum = $items ? max($items) : 0; ?><div
class="col-md-6">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title"><?= h($groupTitle) ?></h3>
</div>
<div class="card-body"><?php if (!$items): ?><div class="text-muted">
<?= h(t('grant.no_data_year', ['year' => $year])) ?></div><?php else: ?><div class="vstack gap-3"><?php foreach ($items as $label => $total): $display = $labels[$label] ?? $label;
$width = $maximum > 0 ? max(5, round($total / $maximum * 100)) : 0; ?><div>
<div class="d-flex justify-content-between gap-3 mb-1">
<span><?= h($display) ?></span><strong><?= (int)$total ?></strong></div>
<div class="progress progress-sm">
<div class="progress-bar bg-primary" style="width:<?= $width ?>%" role="progressbar"
aria-label="<?= h($display) ?>"></div>
</div>
</div><?php endforeach; ?></div><?php endif; ?></div>
</div>
</div><?php endforeach; ?></div>
<form method="post" action="/admin/grants/save" class="card mb-4"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>"><input type="hidden" name="year" value="<?= $year ?>">
<div class="card-header">
<h3 class="card-title"><?= h(t('grant.annual_report', ['year' => $year])) ?></h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-sm-6 col-lg-3"><label class="form-label"><?= h(t('grant.members')) ?></label><input
class="form-control" type="number" min="0" name="members_count"
value="<?= (int)$annual['members_count'] ?>"></div>
<div class="col-sm-6 col-lg-3"><label class="form-label"><?= h(t('grant.volunteers')) ?></label><input
class="form-control" type="number" min="0" name="volunteers_count"
value="<?= (int)$annual['volunteers_count'] ?>"></div>
<div class="col-sm-6 col-lg-3"><label class="form-label"><?= h(t('grant.employees')) ?></label><input
class="form-control" type="number" min="0" name="employees_count"
value="<?= (int)$annual['employees_count'] ?>"></div>
<div class="col-sm-6 col-lg-3"><label class="form-label"><?= h(t('grant.employee_fte')) ?></label><input
class="form-control" type="number" min="0" step="0.01" name="employees_fte"
value="<?= h($annual['employees_fte']) ?>"></div>
<div class="col-md-4"><label class="form-label"><?= h(t('grant.last_ag')) ?></label><input
class="form-control" type="date" lang="fr" name="last_ag_date"
value="<?= h($annual['last_ag_date']) ?>"></div>
<div class="col-md-8"><label class="form-label"><?= h(t('grant.beneficiaries')) ?></label><input
class="form-control" name="beneficiaries" value="<?= h($annual['beneficiaries']) ?>"
placeholder="<?= h(t('grant.beneficiaries_placeholder')) ?>"></div>
<?php foreach (['annual_report' => t('grant.activity_summary'),'completed_actions' => t('grant.completed_actions'),'planned_actions' => t('grant.planned_actions'),'officers_notes' => t('grant.officers'),'in_kind_support' => t('grant.in_kind')] as $field => $label): ?>
<div class="col-md-6"><label class="form-label"><?= h($label) ?></label><textarea class="form-control"
rows="4" name="<?= $field ?>"><?= h($annual[$field]) ?></textarea></div><?php endforeach; ?>
</div>
<hr>
<h3 class="mb-3"><?= h(t('grant.financial_summary')) ?></h3>
<div class="row g-3">
<?php foreach (['actual_expenses' => t('grant.actual_expenses'),'actual_income' => t('grant.actual_income'),'forecast_expenses' => t('grant.forecast_expenses'),'forecast_income' => t('grant.forecast_income'),'treasury_amount' => t('grant.treasury'),'voluntary_contributions' => t('grant.voluntary')] as $field => $label): ?>
<div class="col-sm-6 col-lg-4"><label class="form-label"><?= h($label) ?></label>
<div class="input-group"><input class="form-control" inputmode="decimal" name="<?= $field ?>"
value="<?= h((string)$annual[$field]) ?>"><span class="input-group-text">€</span></div>
</div><?php endforeach; ?></div>
</div>
<div class="card-footer text-end"><button class="btn btn-primary"><?= h(t('grant.save_report')) ?></button></div>
</form>
<div class="card mb-4">
<div class="card-header">
<h3 class="card-title"><?= h(t('grant.applications')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th><?= h(t('grant.organization')) ?></th>
<th><?= h(t('grant.deadline')) ?></th>
<th><?= h(t('grant.requested')) ?></th>
<th><?= h(t('grant.previous_aid')) ?></th>
<th><?= h(t('grant.decision')) ?></th>
<th>Notes</th>
</tr>
</thead>
<tbody><?php if (!$applications): ?><tr>
<td colspan="6" class="text-muted text-center py-4">
<?= h(t('grant.no_application', ['year' => $year])) ?></td>
</tr><?php endif; ?><?php foreach ($applications as $a): ?><tr>
<td class="fw-bold"><?= h($a['organization_name']) ?></td>
<td><?= $date($a['deadline']) ?></td>
<td><?= $money($a['requested_amount']) ?></td>
<td><?= $money($a['previous_amount']) ?></td>
<td>
<form method="post" action="/admin/grants/application" class="d-flex flex-wrap gap-1"><input
type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>"><input type="hidden"
name="year" value="<?= $year ?>"><input type="hidden" name="application_id"
value="<?= (int)$a['id'] ?>"><select class="form-select form-select-sm" name="status"
style="width:auto"><?php foreach (GrantService::STATUSES as $key => $label): ?><option
value="<?= h($key) ?>" <?= $a['status'] === $key ? 'selected' : '' ?>>
<?= h(GrantService::statusLabel($key)) ?></option><?php endforeach; ?></select>
<div class="input-group input-group-sm" style="width:130px"><input class="form-control"
inputmode="decimal" name="awarded_amount"
value="<?= h((string)($a['awarded_amount'] ?? '')) ?>"
placeholder="<?= h(t('grant.awarded')) ?>"><span class="input-group-text">€</span>
</div><button class="btn btn-outline-primary btn-sm">OK</button>
</form>
</td>
<td class="text-muted"><?= nl2br(h($a['notes'])) ?></td>
</tr><?php endforeach; ?></tbody>
</table>
</div>
<div class="card-body border-top">
<form method="post" action="/admin/grants/application" class="row g-3"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>"><input type="hidden" name="year" value="<?= $year ?>">
<div class="col-md-4"><label class="form-label"><?= h(t('grant.organization_required')) ?></label><input
required class="form-control" name="organization_name"></div>
<div class="col-md-2"><label class="form-label"><?= h(t('grant.deadline')) ?></label><input
class="form-control" type="date" lang="fr" name="deadline"></div>
<div class="col-md-2"><label class="form-label"><?= h(t('grant.requested_amount')) ?></label><input
class="form-control" inputmode="decimal" name="requested_amount"></div>
<div class="col-md-2"><label class="form-label"><?= h(t('grant.previous_aid')) ?></label><input
class="form-control" inputmode="decimal" name="previous_amount"></div>
<div class="col-md-2"><label class="form-label"><?= h(t('grant.state')) ?></label><select
class="form-select" name="status"><?php foreach (GrantService::STATUSES as $key => $label): ?>
<option value="<?= h($key) ?>"><?= h($label) ?></option><?php endforeach; ?></select></div>
<div class="col-md-10"><label class="form-label"><?= h(t('grant.notes')) ?></label><input
class="form-control" name="notes"></div>
<div class="col-md-2 d-flex align-items-end"><button
class="btn btn-primary w-100"><?= h(t('common.add')) ?></button></div>
</form>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('grant.checklist')) ?></h3>
<div class="text-muted small"><?= h(t('grant.checklist_help')) ?></div>
</div>
</div>
<div class="card-body">
<div class="row row-cards"><?php if (!$applications): ?><div class="text-muted"><?= h(t('grant.add_first')) ?>
</div>
<?php endif; ?><?php foreach ($applications as $application): $check = $checklist[(int)$application['id']] ?? ['items' => [],'valid' => 0,'total' => count(GrantService::REQUIRED),'complete' => false]; ?>
<div class="col-lg-6">
<div class="card h-100 <?= $check['complete'] ? 'border-success' : 'border-warning' ?>">
<div class="card-status-top <?= $check['complete'] ? 'bg-green' : 'bg-orange' ?>"></div>
<div class="card-header">
<div class="me-auto">
<h3 class="card-title mb-1"><?= h($application['organization_name']) ?></h3>
<div class="text-muted small">
<?= h(t('grant.valid_parts', ['valid' => (int)$check['valid'],'total' => (int)$check['total']])) ?>
</div>
</div><span
class="badge <?= $check['complete'] ? 'bg-green-lt' : 'bg-orange-lt' ?>"><?= $check['complete'] ? t('grant.complete') : t('grant.incomplete') ?></span>
</div>
<div class="card-body">
<div class="row g-2"><?php foreach ($check['items'] as $category => $item): $state = $item['state'];
$meta = match($state) {
'valid' => ['✓','bg-green-lt',t('grant.valid')],'expiring' => ['⌛','bg-yellow-lt',t('grant.expiring')],'expired' => ['!','bg-red-lt',t('grant.expired')],default => ['×','bg-secondary-lt',t('grant.missing')]
}; ?><div class="col-sm-6">
<div class="d-flex align-items-center gap-2 py-1"><span class="badge <?= $meta[1] ?>"
title="<?= h($meta[2]) ?>"><?= $meta[0] ?></span><span
class="small"><?= h(GrantService::categoryLabel($category)) ?></span></div>
</div><?php endforeach; ?></div>
</div>
</div>
</div><?php endforeach; ?></div>
</div>
</div>
<div class="card">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('grant.vault')) ?></h3>
<div class="text-muted small"><?= h(t('grant.vault_help')) ?></div>
</div>
</div>
<div class="card-body">
<div class="row g-3 mb-4"><?php foreach ($documents as $d): ?><div class="col-md-6 col-lg-4"><a
class="card card-link h-100" target="_blank" href="/admin/grants/document?id=<?= (int)$d['id'] ?>">
<div class="card-body">
<div class="d-flex gap-3"><span
class="avatar bg-purple-lt"><?= strtoupper(pathinfo($d['original_name'], PATHINFO_EXTENSION)) ?></span>
<div>
<div class="fw-bold"><?= h($d['title']) ?></div>
<div class="text-muted small"><?= h(GrantService::categoryLabel($d['category'])) ?> ·
<?= $d['year'] ?: t('grant.permanent') ?></div><?php if ($d['application_name']): ?>
<div class="small text-blue">
<?= h(t('grant.folder', ['name' => $d['application_name']])) ?></div><?php endif; ?><?php if ($d['valid_until']): $expired = $d['valid_until'] < date('Y-m-d');
$expiring = !$expired && $d['valid_until'] <= date('Y-m-d', strtotime('+30 days')); ?><div
class="small mt-1 <?= $expired ? 'text-danger fw-bold' : ($expiring ? 'text-warning' : '') ?>">
<?= $expired ? t('grant.expired_on') : t('grant.valid_until') ?>
<?= $date($d['valid_until']) ?></div><?php endif; ?>
</div>
</div>
</div>
</a></div><?php endforeach; ?><?php if (!$documents): ?><div class="text-muted">
<?= h(t('grant.no_document')) ?></div><?php endif; ?></div>
<form method="post" action="/admin/grants/document" enctype="multipart/form-data"
class="row g-3 border-top pt-3"><input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>"><input
type="hidden" name="year" value="<?= $year ?>">
<div class="col-md-3"><label class="form-label"><?= h(t('grant.category')) ?></label><select
class="form-select" name="category"
id="grantDocumentCategory"><?php foreach (GrantService::CATEGORIES as $key => $label): ?><option
value="<?= h($key) ?>"><?= h(GrantService::categoryLabel($key)) ?></option>
<?php endforeach; ?></select></div>
<div class="col-md-3"><label class="form-label"><?= h(t('grant.related_folder')) ?></label><select
class="form-select" name="application_id" id="grantDocumentApplication">
<option value=""><?= h(t('grant.common_part')) ?></option>
<?php foreach ($applications as $application): ?><option value="<?= (int)$application['id'] ?>">
<?= h($application['organization_name']) ?></option><?php endforeach; ?>
</select></div>
<div class="col-md-3"><label class="form-label"><?= h(t('grant.document_title')) ?></label><input
class="form-control" name="title"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('grant.pdf_odt')) ?></label><input required
class="form-control" type="file" name="document"
accept=".pdf,.odt,application/pdf,application/vnd.oasis.opendocument.text"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('grant.valid_until')) ?></label><input
class="form-control" type="date" lang="fr" name="valid_until"></div>
<div class="col-md-6 d-flex align-items-end"><label class="form-check mb-2"><input class="form-check-input"
type="checkbox" name="global_document" value="1" id="grantDocumentGlobal"><span
class="form-check-label"><?= h(t('grant.permanent_document')) ?></span></label></div>
<div class="col-md-3 d-flex align-items-end"><button
class="btn btn-primary w-100"><?= h(t('grant.add_document')) ?></button></div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const category = document.getElementById('grantDocumentCategory'),
application = document.getElementById('grantDocumentApplication'),
global = document.getElementById('grantDocumentGlobal');
const sync = () => {
const official = category?.value === 'official_form';
if (application) application.required = official;
if (global) {
global.disabled = official || Boolean(application?.value);
if (global.disabled) global.checked = false;
}
};
category?.addEventListener('change', sync);
application?.addEventListener('change', sync);
sync();
});
</script>