387 lines
14 KiB
PHP
387 lines
14 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Presentation\Obvod;
|
|
|
|
use App\Model\ObvodFacade;
|
|
use App\Model\ZaznamFacade;
|
|
use App\Presentation\BasePresenter;
|
|
use App\Presentation\Mapa\MapaPresenter;
|
|
use Nette;
|
|
use Nette\Application\UI\Form;
|
|
use Nette\Application\UI\Multiplier;
|
|
use Nette\Utils\ArrayHash;
|
|
|
|
|
|
final class ObvodPresenter extends BasePresenter
|
|
{
|
|
private const RAZENI_COOKIE = 'obvodRazeni';
|
|
|
|
/** @var array<string, string> */
|
|
private const RAZENI = [
|
|
'cislo-vzestupne' => 'Číslo vzestupně',
|
|
'cislo-sestupne' => 'Číslo sestupně',
|
|
'rozpracovani-sestupne' => 'Rozpracování sestupně',
|
|
'rozpracovani-vzestupne' => 'Rozpracování vzestupně',
|
|
'nepropracovani-vzestupne' => 'Nepropracování vzestupně',
|
|
'nepropracovani-sestupne' => 'Nepropracování sestupně',
|
|
];
|
|
|
|
private string $skupina = '';
|
|
|
|
private string $razeni = 'cislo-vzestupne';
|
|
|
|
public function __construct(
|
|
private ObvodFacade $obvod,
|
|
private ZaznamFacade $zaznam
|
|
) {
|
|
}
|
|
|
|
public function beforeRender(): void
|
|
{
|
|
parent::beforeRender();
|
|
}
|
|
|
|
public function actionList(?string $skupina = null, ?string $razeni = null): void
|
|
{
|
|
$this->skupina = $this->obvod->vyberSkupinu(
|
|
$skupina,
|
|
$this->getHttpRequest()->getCookie(ObvodFacade::SKUPINA_COOKIE),
|
|
);
|
|
if ($skupina !== null && $skupina === $this->skupina && $this->skupina !== '') {
|
|
$this->getHttpResponse()->setCookie(ObvodFacade::SKUPINA_COOKIE, $this->skupina, '+1 year', '/');
|
|
}
|
|
|
|
if ($razeni !== null && isset(self::RAZENI[$razeni])) {
|
|
$this->razeni = $razeni;
|
|
$this->getHttpResponse()->setCookie(self::RAZENI_COOKIE, $razeni, '+1 year', '/');
|
|
} else {
|
|
$ulozeny = $this->getHttpRequest()->getCookie(self::RAZENI_COOKIE);
|
|
$this->razeni = is_string($ulozeny) && isset(self::RAZENI[$ulozeny]) ? $ulozeny : 'cislo-vzestupne';
|
|
}
|
|
}
|
|
|
|
public function renderList(): void
|
|
{
|
|
$stari = $this->zaznam->getStariObvodu();
|
|
$obvody = [];
|
|
foreach ($this->obvod->getObvody($this->skupina) as $obvod) {
|
|
$info = $stari[(int) $obvod->CISLO] ?? null;
|
|
$rozpracovan = $info !== null && $info['probiha'];
|
|
$obvody[] = (object) [
|
|
'ID' => (int) $obvod->ID,
|
|
'CISLO' => (int) $obvod->CISLO,
|
|
'NAZEV' => (string) $obvod->NAZEV,
|
|
'stav' => $this->stavObvodu($info),
|
|
'rozpracovani' => $rozpracovan ? (int) ($info['prideleni'] ?? 0) : null,
|
|
'nepropracovani' => !$rozpracovan && $info !== null && $info['mesice'] !== null
|
|
? (int) $info['mesice']
|
|
: null,
|
|
];
|
|
}
|
|
|
|
$this->seradObvody($obvody);
|
|
$this->template->skupina = $this->skupina;
|
|
$this->template->skupiny = $this->obvod->getSkupiny();
|
|
$this->template->razeni = $this->razeni;
|
|
$this->template->razeniMoznosti = self::RAZENI;
|
|
$this->template->obvody = $obvody;
|
|
}
|
|
|
|
public function renderView(int $id = null): void
|
|
{
|
|
$sousede = $id === null
|
|
? ['predchozi' => null, 'dalsi' => null]
|
|
: $this->obvod->getSousede($id);
|
|
$this->template->obvod = $this->obvod->getObvod($id);
|
|
$this->template->predchozi = $sousede['predchozi'];
|
|
$this->template->dalsi = $sousede['dalsi'];
|
|
$this->template->zaznamy = $this->zaznam->getZaznamyObvodu($id);
|
|
$this->template->zvestovateleJson = json_encode(
|
|
array_values($this->zaznam->getZvestovatele()),
|
|
JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_THROW_ON_ERROR
|
|
);
|
|
$this->template->otevrene = $id === null ? [] : $this->zaznam->getOtevrene($id);
|
|
}
|
|
|
|
protected function createComponentPridelitForm(): Form
|
|
{
|
|
$form = new Form;
|
|
$form->addText('zvestovatel', 'Zvěstovatel')
|
|
->setRequired('Zadejte zvěstovatele.')
|
|
->setMaxLength(100);
|
|
$form->addDate('datum', 'Datum přidělení')
|
|
->setRequired('Zadejte datum přidělení.')
|
|
->setDefaultValue(new \DateTimeImmutable('today'));
|
|
$form->addSubmit('send', 'Přidělit');
|
|
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
|
|
$form->onSuccess[] = $this->pridelitFormSucceeded(...);
|
|
return $form;
|
|
}
|
|
|
|
private function pridelitFormSucceeded(Form $form, ArrayHash $values): void
|
|
{
|
|
$id = $this->obvodId();
|
|
$jmeno = trim((string) $values->zvestovatel);
|
|
if ($jmeno === '') {
|
|
$form->addError('Zadejte zvěstovatele.');
|
|
return;
|
|
}
|
|
if (mb_strlen($jmeno) > 100) {
|
|
$form->addError('Jméno zvěstovatele je příliš dlouhé.');
|
|
return;
|
|
}
|
|
|
|
$this->zaznam->pridej($id, $jmeno, $this->den($values->datum));
|
|
$this->flashMessage('Obvod byl přidělen.');
|
|
$this->redirect('this');
|
|
}
|
|
|
|
protected function createComponentVratitForm(): Form
|
|
{
|
|
$form = new Form;
|
|
$polozky = [];
|
|
foreach ($this->zaznam->getOtevrene($this->obvodId()) as $zaznam) {
|
|
$polozky[$zaznam->ID] = $zaznam->JMENO . ' (přiděleno ' . $this->den($zaznam->VYDANO)->format('d.m.Y') . ')';
|
|
}
|
|
$form->addSelect('zaznam', 'Záznam', $polozky)
|
|
->setRequired('Vyberte záznam k vrácení.');
|
|
$form->addDate('datum', 'Datum vrácení')
|
|
->setRequired('Zadejte datum vrácení.')
|
|
->setDefaultValue(new \DateTimeImmutable('today'));
|
|
$form->addSubmit('send', 'Vrátit');
|
|
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
|
|
$form->onSuccess[] = $this->vratitFormSucceeded(...);
|
|
return $form;
|
|
}
|
|
|
|
private function vratitFormSucceeded(Form $form, ArrayHash $values): void
|
|
{
|
|
$obvodId = $this->obvodId();
|
|
$zaznam = $this->zaznam->getZaznam((int) $values->zaznam);
|
|
if ($zaznam === null || (int) $zaznam->OBVOD !== $obvodId || $zaznam->VRACENO !== null) {
|
|
$form->addError('Tento záznam už nelze vrátit.');
|
|
return;
|
|
}
|
|
|
|
$vraceno = $this->den($values->datum);
|
|
if ($this->den($zaznam->VYDANO) > $vraceno) {
|
|
$form->addError('Datum přidělení nesmí být později než datum vrácení.');
|
|
return;
|
|
}
|
|
|
|
$this->zaznam->vrat((int) $zaznam->ID, $obvodId, $vraceno);
|
|
$this->flashMessage('Obvod byl vrácen.');
|
|
$this->redirect('this');
|
|
}
|
|
|
|
protected function createComponentUpravitForm(): Form
|
|
{
|
|
$form = new Form;
|
|
$form->addHidden('id');
|
|
$form->addText('zvestovatel', 'Zvěstovatel')
|
|
->setRequired('Zadejte zvěstovatele.')
|
|
->setMaxLength(100);
|
|
$form->addDate('vydano', 'Datum přidělení')
|
|
->setRequired('Zadejte datum přidělení.');
|
|
$form->addDate('vraceno', 'Datum vrácení');
|
|
$form->addSubmit('send', 'Uložit');
|
|
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
|
|
$form->onSuccess[] = $this->upravitFormSucceeded(...);
|
|
return $form;
|
|
}
|
|
|
|
private function upravitFormSucceeded(Form $form, ArrayHash $values): void
|
|
{
|
|
$obvodId = $this->obvodId();
|
|
$zaznam = $this->zaznam->getZaznam((int) $values->id);
|
|
if ($zaznam === null || (int) $zaznam->OBVOD !== $obvodId) {
|
|
$form->addError('Tento záznam už nelze upravit.');
|
|
return;
|
|
}
|
|
|
|
$jmeno = trim((string) $values->zvestovatel);
|
|
if ($jmeno === '') {
|
|
$form->addError('Zadejte zvěstovatele.');
|
|
return;
|
|
}
|
|
if (mb_strlen($jmeno) > 100) {
|
|
$form->addError('Jméno zvěstovatele je příliš dlouhé.');
|
|
return;
|
|
}
|
|
|
|
$vydano = $this->den($values->vydano);
|
|
$vraceno = $values->vraceno !== null && $values->vraceno !== ''
|
|
? $this->den($values->vraceno)
|
|
: null;
|
|
if ($vraceno !== null && $vydano > $vraceno) {
|
|
$form->addError('Datum přidělení nesmí být později než datum vrácení.');
|
|
return;
|
|
}
|
|
|
|
if (!$this->zaznam->uprav((int) $zaznam->ID, $obvodId, $jmeno, $vydano, $vraceno)) {
|
|
$form->addError('Tento záznam už nelze upravit.');
|
|
return;
|
|
}
|
|
|
|
$this->flashMessage('Záznam byl upraven.');
|
|
$this->redirect('this');
|
|
}
|
|
|
|
protected function createComponentSmazat(): Multiplier
|
|
{
|
|
return new Multiplier(function (string $id): Form {
|
|
$form = new Form;
|
|
$form->addSubmit('send', 'Smazat');
|
|
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
|
|
$form->onSuccess[] = function () use ($id): void {
|
|
if ($this->zaznam->smaz((int) $id, $this->obvodId())) {
|
|
$this->flashMessage('Záznam byl smazán.');
|
|
} else {
|
|
$this->flashMessage('Tento záznam už nelze smazat.');
|
|
}
|
|
$this->redirect('this');
|
|
};
|
|
return $form;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param list<object> $obvody
|
|
*/
|
|
private function seradObvody(array &$obvody): void
|
|
{
|
|
$razeni = $this->razeni;
|
|
$sestupne = str_ends_with($razeni, 'sestupne');
|
|
$klic = match (true) {
|
|
str_starts_with($razeni, 'rozpracovani') => 'rozpracovani',
|
|
str_starts_with($razeni, 'nepropracovani') => 'nepropracovani',
|
|
default => null,
|
|
};
|
|
|
|
usort($obvody, function (object $a, object $b) use ($sestupne, $klic): int {
|
|
if ($klic === null) {
|
|
$poradi = $a->CISLO <=> $b->CISLO;
|
|
return $sestupne ? -$poradi : $poradi;
|
|
}
|
|
|
|
$prvni = $a->{$klic};
|
|
$druhy = $b->{$klic};
|
|
if ($prvni === null && $druhy === null) {
|
|
return $a->CISLO <=> $b->CISLO;
|
|
}
|
|
if ($prvni === null) {
|
|
return 1;
|
|
}
|
|
if ($druhy === null) {
|
|
return -1;
|
|
}
|
|
|
|
$poradi = $prvni <=> $druhy;
|
|
if ($poradi === 0) {
|
|
return $a->CISLO <=> $b->CISLO;
|
|
}
|
|
|
|
return $sestupne ? -$poradi : $poradi;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param array{mesice: int|null, probiha: bool, prideleni: int|null}|null $info
|
|
* @return object{popisek: string, barva: string, text: string}
|
|
*/
|
|
private function stavObvodu(?array $info): object
|
|
{
|
|
if ($info !== null && $info['probiha']) {
|
|
$mesice = $info['prideleni'];
|
|
return (object) [
|
|
'popisek' => 'Rozpracován ' . $this->textMesicu($mesice),
|
|
'barva' => $this->cssBarva($this->barvaStari($mesice, MapaPresenter::STUPNICE_PRIDELENI)),
|
|
'text' => $this->cssBarva($this->barvaTextu($mesice, MapaPresenter::STUPNICE_PRIDELENI)),
|
|
];
|
|
}
|
|
|
|
if ($info !== null && $info['mesice'] !== null) {
|
|
return (object) [
|
|
'popisek' => 'Nepropracován ' . $this->textMesicu($info['mesice']),
|
|
'barva' => $this->cssBarva($this->barvaStari($info['mesice'], MapaPresenter::STUPNICE_MESICU)),
|
|
'text' => $this->cssBarva($this->barvaTextu($info['mesice'], MapaPresenter::STUPNICE_MESICU)),
|
|
];
|
|
}
|
|
|
|
return (object) [
|
|
'popisek' => 'Bez záznamu',
|
|
'barva' => '#b0bec5',
|
|
'text' => '#1a1a1a',
|
|
];
|
|
}
|
|
|
|
private function cssBarva(string $barva): string
|
|
{
|
|
if (preg_match('/^rgb\((?:25[0-5]|2[0-4]\d|1?\d?\d),(?:25[0-5]|2[0-4]\d|1?\d?\d),(?:25[0-5]|2[0-4]\d|1?\d?\d)\)$/', $barva)) {
|
|
return $barva;
|
|
}
|
|
if (preg_match('/^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?$/', $barva)) {
|
|
return $barva;
|
|
}
|
|
|
|
return '#b0bec5';
|
|
}
|
|
|
|
private function textMesicu(?int $mesice): string
|
|
{
|
|
return $mesice === null ? '—' : $mesice . ' měs.';
|
|
}
|
|
|
|
private function barvaStari(?int $mesice, int $max): string
|
|
{
|
|
$t = $mesice === null || $max <= 0 ? 1 : max(0, min($mesice / $max, 1));
|
|
$zelena = [27, 158, 62];
|
|
$zluta = [230, 184, 0];
|
|
$cervena = [198, 40, 40];
|
|
$kanaly = $t < 0.5
|
|
? $this->mixBarvy($zelena, $zluta, $t / 0.5)
|
|
: $this->mixBarvy($zluta, $cervena, ($t - 0.5) / 0.5);
|
|
|
|
return 'rgb(' . implode(',', $kanaly) . ')';
|
|
}
|
|
|
|
private function barvaTextu(?int $mesice, int $max): string
|
|
{
|
|
$t = $mesice === null || $max <= 0 ? 1 : max(0, min($mesice / $max, 1));
|
|
return $t > 0.72 ? '#fff' : '#1a1a1a';
|
|
}
|
|
|
|
/**
|
|
* @param array{0: int, 1: int, 2: int} $a
|
|
* @param array{0: int, 1: int, 2: int} $b
|
|
* @return array{0: int, 1: int, 2: int}
|
|
*/
|
|
private function mixBarvy(array $a, array $b, float $t): array
|
|
{
|
|
return [
|
|
(int) round($a[0] + ($b[0] - $a[0]) * $t),
|
|
(int) round($a[1] + ($b[1] - $a[1]) * $t),
|
|
(int) round($a[2] + ($b[2] - $a[2]) * $t),
|
|
];
|
|
}
|
|
|
|
private function obvodId(): int
|
|
{
|
|
$id = $this->getParameter('id');
|
|
if ($id === null || $id === '') {
|
|
$this->error('Obvod nenalezen.', Nette\Http\IResponse::S404_NotFound);
|
|
}
|
|
return (int) $id;
|
|
}
|
|
|
|
private function den(mixed $value): \DateTimeImmutable
|
|
{
|
|
$datum = $value instanceof \DateTimeInterface
|
|
? \DateTimeImmutable::createFromInterface($value)
|
|
: new \DateTimeImmutable((string) $value);
|
|
return $datum->setTime(0, 0);
|
|
}
|
|
}
|