feature/RVRNEXT-11-events #54

Merged
bence merged 13 commits from feature/RVRNEXT-11-events into master 2023-05-28 16:52:49 +02:00
24 changed files with 748 additions and 38 deletions

4
Jenkinsfile vendored
View File

@ -35,7 +35,7 @@ pipeline {
sh 'vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests' sh 'vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests'
} }
post { post {
success { always {
archiveArtifacts 'unit_test_results.xml' archiveArtifacts 'unit_test_results.xml'
} }
} }
@ -53,7 +53,7 @@ pipeline {
sh 'php vendor/bin/phpstan analyse -c phpstan.neon --error-format=prettyJson > static_code_analysis_results.json' sh 'php vendor/bin/phpstan analyse -c phpstan.neon --error-format=prettyJson > static_code_analysis_results.json'
} }
post { post {
success { always {
archiveArtifacts 'static_code_analysis_results.json' archiveArtifacts 'static_code_analysis_results.json'
} }
} }

View File

@ -10,7 +10,7 @@
} }
], ],
"require": { "require": {
"esoko/soko-web": "0.11", "esoko/soko-web": "0.12.1",
"firebase/php-jwt": "^6.4" "firebase/php-jwt": "^6.4"
}, },
"require-dev": { "require-dev": {

8
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c17dd5eb82ff8f509a17404bd4c471b8", "content-hash": "a6f50b31b14027eec094629b13cc4097",
"packages": [ "packages": [
{ {
"name": "cocur/slugify", "name": "cocur/slugify",
@ -82,11 +82,11 @@
}, },
{ {
"name": "esoko/soko-web", "name": "esoko/soko-web",
"version": "v0.11", "version": "v0.12.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://git.esoko.eu/esoko/soko-web.git", "url": "https://git.esoko.eu/esoko/soko-web.git",
"reference": "78f891fbff0dc8e5de5607529f4c8282f1dff40f" "reference": "bda12177ebc201f04fdad5493b93039676a983ca"
}, },
"require": { "require": {
"cocur/slugify": "^4.3", "cocur/slugify": "^4.3",
@ -108,7 +108,7 @@
"GNU GPL 3.0" "GNU GPL 3.0"
], ],
"description": "Lightweight web framework", "description": "Lightweight web framework",
"time": "2023-05-06T23:52:34+00:00" "time": "2023-05-28T13:56:14+00:00"
}, },
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",

View File

@ -0,0 +1,18 @@
CREATE TABLE `events` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`slug` varchar(255) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL,
`community_id` int(10) unsigned NOT NULL,
`start` timestamp NOT NULL DEFAULT current_timestamp(),
`end` timestamp NOT NULL DEFAULT current_timestamp(),
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`id`),
KEY `community_id` (`community_id`),
UNIQUE KEY `slug` (`slug`),
CONSTRAINT `events_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
ALTER TABLE `transactions`
ADD `event_id` int(10) unsigned NULL,
ADD KEY `event_id` (`event_id`) AFTER `community_id`,
ADD CONSTRAINT `transactions_event_id` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`);

View File

@ -108,6 +108,10 @@ p.small, span.small, td.small {
font-size: 14px; font-size: 14px;
} }
p.big, span.big, td.big {
font-size: 18px;
}
.red { .red {
color: #a80908; color: #a80908;
} }
@ -255,11 +259,11 @@ button.red:enabled:hover, button.red:enabled:focus, a.button.red:hover, a.button
} }
button.yellow, a.button.yellow { button.yellow, a.button.yellow {
background-color: #e8a349; background-color: #daa520;
} }
button.yellow:enabled:hover, button.yellow:enabled:focus, a.button.yellow:hover, a.button.yellow:focus { button.yellow:enabled:hover, button.yellow:enabled:focus, a.button.yellow:hover, a.button.yellow:focus {
background-color: #c37713; background-color: #b8860b;
} }
button.green, a.button.green { button.green, a.button.green {
@ -397,7 +401,7 @@ header>p {
} }
header>p>span { header>p>span {
padding-left: 6px; padding-left: 8px;
} }
header>p>span>a:link, header>p>span>a:visited { header>p>span>a:link, header>p>span>a:visited {
@ -410,7 +414,7 @@ header>p>span>a:hover, header>p>span>a:focus {
header>p>span:not(:last-child) { header>p>span:not(:last-child) {
border-right: solid white 1px; border-right: solid white 1px;
padding-right: 6px; padding-right: 8px;
} }
main { main {
@ -537,6 +541,15 @@ p.formLabel {
font-size: 14px; font-size: 14px;
} }
span.label {
border-radius: 3px;
background-color: #555555;
color: #ffffff;
padding: 2px 4px;
font-weight: bold;
font-size: 13px;
}
@media screen and (max-width: 424px) { @media screen and (max-width: 424px) {
div.gridContainer { div.gridContainer {
grid-template-columns: auto; grid-template-columns: auto;

View File

@ -0,0 +1,30 @@
(function () {
const element = document.getElementById('transactionForm').elements['event_id'];
const select = new TomSelect(element, {
valueField: 'value',
labelField: 'label',
searchField: 'label',
loadThrottle: 300,
load: function (query, callback) {
var self = this;
RVR.httpRequest('GET', searchEventUrl.replace('QUERY', encodeURIComponent(query)), function () {
self.clearOptions();
callback(this.response.results);
});
},
});
select.on('change', function (value) {
this.clearOptions();
});
select.on('blur', function (value) {
this.clearOptions();
});
select.on('type', function (value) {
if (value === '') {
this.clearOptions();
}
});
})();

View File

@ -11,6 +11,7 @@ use RVR\Repository\CommunityRepository;
use RVR\Repository\CommunityMemberRepository; use RVR\Repository\CommunityMemberRepository;
use RVR\Repository\CurrencyExchangeRateRepository; use RVR\Repository\CurrencyExchangeRateRepository;
use RVR\Repository\CurrencyRepository; use RVR\Repository\CurrencyRepository;
use RVR\Repository\EventRepository;
use RVR\Repository\TransactionRepository; use RVR\Repository\TransactionRepository;
use RVR\Repository\UserRepository; use RVR\Repository\UserRepository;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired; use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
@ -32,6 +33,8 @@ class CommunityController implements IAuthenticationRequired
private TransactionRepository $transactionRepository; private TransactionRepository $transactionRepository;
private EventRepository $eventRepository;
public function __construct() public function __construct()
{ {
$this->userRepository = new UserRepository(); $this->userRepository = new UserRepository();
@ -40,6 +43,7 @@ class CommunityController implements IAuthenticationRequired
$this->currencyRepository = new CurrencyRepository(); $this->currencyRepository = new CurrencyRepository();
$this->currencyExchangeRatesRepository = new CurrencyExchangeRateRepository(); $this->currencyExchangeRatesRepository = new CurrencyExchangeRateRepository();
$this->transactionRepository = new TransactionRepository(); $this->transactionRepository = new TransactionRepository();
$this->eventRepository = new EventRepository();
} }
public function isAuthenticationRequired(): bool public function isAuthenticationRequired(): bool
@ -75,7 +79,7 @@ class CommunityController implements IAuthenticationRequired
return new HtmlContent('communities/community', [ return new HtmlContent('communities/community', [
'community' => $community, 'community' => $community,
'upcomingEvents' => [], 'upcomingEvents' => iterator_to_array($this->eventRepository->getUpcomingByCommunity($community, new DateTime(), 3)),
'debtItems' => $debtItems, 'debtItems' => $debtItems,
'debtBalance' => $debtBalance, 'debtBalance' => $debtBalance,
'outstandingItems' => $outstandingItems, 'outstandingItems' => $outstandingItems,

View File

@ -0,0 +1,181 @@
<?php namespace RVR\Controller;
use Container;
use DateTime;
use RVR\Finance\ExchangeRateCalculator;
use RVR\PersistentData\Model\Community;
use RVR\PersistentData\Model\CommunityMember;
use RVR\PersistentData\Model\Event;
use RVR\PersistentData\Model\User;
use RVR\Repository\CommunityMemberRepository;
use RVR\Repository\CommunityRepository;
use RVR\Repository\EventRepository;
use RVR\Repository\TransactionRepository;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
use SokoWeb\Interfaces\Authorization\ISecured;
use SokoWeb\Interfaces\Response\IContent;
use SokoWeb\Response\HtmlContent;
use SokoWeb\Response\JsonContent;
class EventController implements IAuthenticationRequired, ISecured
{
private CommunityRepository $communityRepository;
private CommunityMemberRepository $communityMemberRepository;
private EventRepository $eventRepository;
private TransactionRepository $transactionRepository;
private ?Community $community;
private ?CommunityMember $ownCommunityMember;
public function __construct()
{
$this->communityRepository = new CommunityRepository();
$this->communityMemberRepository = new CommunityMemberRepository();
$this->eventRepository = new EventRepository();
$this->transactionRepository = new TransactionRepository();
}
public function isAuthenticationRequired(): bool
{
return true;
}
public function authorize(): bool
{
$communitySlug = \Container::$request->query('communitySlug');
$this->community = $this->communityRepository->getBySlug($communitySlug);
if ($this->community === null) {
return false;
}
/**
* @var User $user
*/
$user = \Container::$request->user();
$this->ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($this->community, $user);
if ($this->ownCommunityMember === null) {
return false;
}
return true;
}
public function getEvents(): IContent
{
$itemsPerPage = 10;
$numberOfEvents = $this->eventRepository->countAllByCommunity($this->community);
$pages = ceil($numberOfEvents / $itemsPerPage);
$currentPage = Container::$request->query('page') ?: 0;
$events = $this->eventRepository->getPagedByCommunity(
$this->community,
$currentPage * $itemsPerPage,
$itemsPerPage
);
return new HtmlContent('events/events', [
'community' => $this->community,
'pages' => $pages,
'currentPage' => $currentPage,
'numberOfEvents' => $numberOfEvents,
'events' => $events
]);
}
public function searchEvent(): IContent
{
$events = iterator_to_array($this->eventRepository->searchByTitle($this->community, Container::$request->query('q')));
$results = [];
foreach ($events as $event) {
$results[] = ['value' => $event->getId(), 'label' => $event->getTitle()];
}
return new JsonContent([
'results' => $results
]);
}
public function getEvent(): ?IContent
{
$event = $this->eventRepository->getBySlug(Container::$request->query('eventSlug'));
if (!$event) {
return null;
}
Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']);
return new HtmlContent('events/event', [
'community' => $this->community,
'event' => $event,
'totalCost' => $this->sumTransactions($event)
]);
}
public function getEventEdit(): ?IContent
{
$eventSlug = Container::$request->query('eventSlug');
if ($eventSlug) {
$event = $this->eventRepository->getBySlug($eventSlug);
if ($event === null) {
return null;
}
} else {
$event = null;
}
return new HtmlContent('events/event_edit', [
'community' => $this->community,
'event' => $event
]);
}
public function saveEvent(): ?IContent
{
$eventSlug = Container::$request->query('eventSlug');
if ($eventSlug) {
$event = $this->eventRepository->getBySlug($eventSlug);
} else {
$event = new Event();
$event->setCommunity($this->community);
}
$event->setTitle(Container::$request->post('title'));
$event->setDescription(Container::$request->post('description'));
$event->setStartDate(new DateTime(Container::$request->post('start')));
$event->setEndDate(new DateTime(Container::$request->post('end')));
Container::$persistentDataManager->saveToDb($event);
return new JsonContent([
'redirect' => ['target' => \Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $this->community->getSlug(), 'eventSlug' => $event->getSlug()])]
]);
}
public function deleteEvent(): IContent
{
$event = $this->eventRepository->getBySlug(Container::$request->query('eventSlug'));
foreach ($this->transactionRepository->getAllByEvent($event) as $transaction) {
$transaction->setEventId(null);
Container::$persistentDataManager->saveToDb($transaction);
}
Container::$persistentDataManager->deleteFromDb($event);
return new JsonContent(['success' => true]);
}
private function sumTransactions(Event $event): float
{
$exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());
$transactions = $this->transactionRepository->getAllByEvent($event, true, ['currency']);
$sum = 0.0;
foreach ($transactions as $transaction) {
$sum += $exchangeRateCalculator->calculate($transaction->getSum(), $transaction->getCurrency(), $transaction->getTimeDate());
}
return $sum;
}
}

View File

@ -1,8 +1,10 @@
<?php namespace RVR\Controller; <?php namespace RVR\Controller;
use DateTime;
use RVR\PersistentData\Model\Community; use RVR\PersistentData\Model\Community;
use RVR\PersistentData\Model\User; use RVR\PersistentData\Model\User;
use RVR\Repository\CommunityMemberRepository; use RVR\Repository\CommunityMemberRepository;
use RVR\Repository\EventRepository;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired; use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
use SokoWeb\Interfaces\Response\IContent; use SokoWeb\Interfaces\Response\IContent;
use SokoWeb\Response\HtmlContent; use SokoWeb\Response\HtmlContent;
@ -11,9 +13,12 @@ class HomeController implements IAuthenticationRequired
{ {
private CommunityMemberRepository $communityMemberRepository; private CommunityMemberRepository $communityMemberRepository;
private EventRepository $eventRepository;
public function __construct() public function __construct()
{ {
$this->communityMemberRepository = new CommunityMemberRepository(); $this->communityMemberRepository = new CommunityMemberRepository();
$this->eventRepository = new EventRepository();
} }
public function isAuthenticationRequired(): bool public function isAuthenticationRequired(): bool
@ -39,7 +44,7 @@ class HomeController implements IAuthenticationRequired
return new HtmlContent('home', [ return new HtmlContent('home', [
'communities' => $communities, 'communities' => $communities,
'upcomingEvents' => [] 'upcomingEvents' => iterator_to_array($this->eventRepository->getUpcomingByUser($user, new DateTime(), 3, true, ['community']))
]); ]);
} }
} }

View File

@ -11,6 +11,7 @@ use RVR\Repository\CommunityMemberRepository;
use RVR\Repository\CommunityRepository; use RVR\Repository\CommunityRepository;
use RVR\Repository\CurrencyRepository; use RVR\Repository\CurrencyRepository;
use RVR\Repository\TransactionRepository; use RVR\Repository\TransactionRepository;
use RVR\Repository\EventRepository;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired; use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
use SokoWeb\Interfaces\Authorization\ISecured; use SokoWeb\Interfaces\Authorization\ISecured;
use SokoWeb\Interfaces\Response\IContent; use SokoWeb\Interfaces\Response\IContent;
@ -27,9 +28,11 @@ class TransactionController implements IAuthenticationRequired, ISecured
private TransactionRepository $transactionRepository; private TransactionRepository $transactionRepository;
private Community $community; private EventRepository $eventRepository;
private CommunityMember $ownCommunityMember; private ?Community $community;
private ?CommunityMember $ownCommunityMember;
public function __construct() public function __construct()
{ {
@ -37,6 +40,7 @@ class TransactionController implements IAuthenticationRequired, ISecured
$this->communityMemberRepository = new CommunityMemberRepository(); $this->communityMemberRepository = new CommunityMemberRepository();
$this->currencyRepository = new CurrencyRepository(); $this->currencyRepository = new CurrencyRepository();
$this->transactionRepository = new TransactionRepository(); $this->transactionRepository = new TransactionRepository();
$this->eventRepository = new EventRepository();
} }
public function isAuthenticationRequired(): bool public function isAuthenticationRequired(): bool
@ -69,20 +73,38 @@ class TransactionController implements IAuthenticationRequired, ISecured
Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']); Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']);
$exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency()); $exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());
$eventSlug = Container::$request->query('event');
if ($eventSlug) {
$event = $this->eventRepository->getBySlug($eventSlug);
} else {
$event = null;
}
$itemsPerPage = 50; $itemsPerPage = 50;
$numberOfTransactions = $this->transactionRepository->countAllByCommunity($this->community); $numberOfTransactions = $event ?
$this->transactionRepository->countAllByEvent($event) :
$this->transactionRepository->countAllByCommunity($this->community);
$pages = ceil($numberOfTransactions / $itemsPerPage); $pages = ceil($numberOfTransactions / $itemsPerPage);
$currentPage = Container::$request->query('page') ?: 0; $currentPage = Container::$request->query('page') ?: 0;
$transactions = $this->transactionRepository->getPagedByCommunity( $transactions = $event ?
$this->community, $this->transactionRepository->getPagedByEvent(
$currentPage * $itemsPerPage, $event,
$itemsPerPage, $currentPage * $itemsPerPage,
true, $itemsPerPage,
['currency', 'payer_user', 'payee_user'] true,
); ['currency', 'payer_user', 'payee_user']
) :
$this->transactionRepository->getPagedByCommunity(
$this->community,
$currentPage * $itemsPerPage,
$itemsPerPage,
true,
['event', 'currency', 'payer_user', 'payee_user']
);
return new HtmlContent('communities/transactions', [ return new HtmlContent('communities/transactions', [
'community' => $this->community, 'community' => $this->community,
'event' => $event,
'exchangeRateCalculator' => $exchangeRateCalculator, 'exchangeRateCalculator' => $exchangeRateCalculator,
'pages' => $pages, 'pages' => $pages,
'currentPage' => $currentPage, 'currentPage' => $currentPage,
@ -99,13 +121,22 @@ class TransactionController implements IAuthenticationRequired, ISecured
if ($transaction === null) { if ($transaction === null) {
return null; return null;
} }
Container::$persistentDataManager->loadRelationsFromDb($transaction, false, ['event']);
$event = $transaction->getEvent();
} else { } else {
$transaction = null; $transaction = null;
$eventSlug = Container::$request->query('event');
if ($eventSlug) {
$event = $this->eventRepository->getBySlug($eventSlug);
} else {
$event = null;
}
} }
return new HtmlContent('communities/transaction_edit', [ return new HtmlContent('communities/transaction_edit', [
'community' => $this->community, 'community' => $this->community,
'transaction' => $transaction, 'transaction' => $transaction,
'event' => $event,
'members' => $this->getMembers($this->community), 'members' => $this->getMembers($this->community),
'currencies' => $this->getCurrencies($this->community) 'currencies' => $this->getCurrencies($this->community)
]); ]);
@ -121,6 +152,7 @@ class TransactionController implements IAuthenticationRequired, ISecured
$transaction->setCommunity($this->community); $transaction->setCommunity($this->community);
} }
$transaction->setEventId(Container::$request->post('event_id') ?: null);
$transaction->setCurrencyId(Container::$request->post('currency_id')); $transaction->setCurrencyId(Container::$request->post('currency_id'));
$transaction->setPayerUserId(Container::$request->post('payer_user_id')); $transaction->setPayerUserId(Container::$request->post('payer_user_id'));
$transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null); $transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null);
@ -143,7 +175,7 @@ class TransactionController implements IAuthenticationRequired, ISecured
private function getMembers(Community $community): array private function getMembers(Community $community): array
{ {
$members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user'])); $members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user']));
usort($members, function($a, $b) { usort($members, function ($a, $b) {
return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName()); return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
}); });
return $members; return $members;
@ -152,10 +184,10 @@ class TransactionController implements IAuthenticationRequired, ISecured
private function getCurrencies(Community $community): array private function getCurrencies(Community $community): array
{ {
$currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community)); $currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community));
usort($currencies, function($a, $b) { usort($currencies, function ($a, $b) {
return strnatcmp($a->getCode(), $b->getCode()); return strnatcmp($a->getCode(), $b->getCode());
}); });
usort($currencies, function($a, $b) use ($community) { usort($currencies, function ($a, $b) use ($community) {
return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId()); return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId());
}); });
return $currencies; return $currencies;

View File

@ -0,0 +1,107 @@
<?php namespace RVR\PersistentData\Model;
use DateTime;
use SokoWeb\PersistentData\Model\ModelWithSlug;
class Event extends ModelWithSlug
{
protected static string $table = 'events';
protected static array $fields = ['community_id', 'start', 'end', 'title', 'description'];
protected static array $relations = ['community' => Community::class];
protected static string $slugSource = 'title';
private ?Community $community = null;
private int $communityId;
private DateTime $start;
private DateTime $end;
private string $title = '';
private string $description = '';
public function setCommunity(Community $community): void
{
$this->community = $community;
}
public function setCommunityId(int $communityId): void
{
$this->communityId = $communityId;
}
public function setStartDate(DateTime $start): void
{
$this->start = $start;
}
public function setStart(string $start): void
{
$this->start = new DateTime($start);
}
public function setEndDate(DateTime $end): void
{
$this->end = $end;
}
public function setEnd(string $end): void
{
$this->end = new DateTime($end);
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function setDescription(string $description): void
{
$this->description = $description;
}
public function getCommunity(): ?Community
{
return $this->community;
}
public function getCommunityId(): int
{
return $this->communityId;
}
public function getStartDate(): DateTime
{
return $this->start;
}
public function getStart(): string
{
return $this->start->format('Y-m-d H:i:s');
}
public function getEndDate(): DateTime
{
return $this->end;
}
public function getEnd(): string
{
return $this->end->format('Y-m-d H:i:s');
}
public function getTitle(): string
{
return $this->title;
}
public function getDescription(): string
{
return $this->description;
}
}

View File

@ -7,10 +7,11 @@ class Transaction extends Model
{ {
protected static string $table = 'transactions'; protected static string $table = 'transactions';
protected static array $fields = ['community_id', 'currency_id', 'payer_user_id', 'payee_user_id', 'description', 'sum', 'time']; protected static array $fields = ['community_id', 'event_id', 'currency_id', 'payer_user_id', 'payee_user_id', 'description', 'sum', 'time'];
protected static array $relations = [ protected static array $relations = [
'community' => Community::class, 'community' => Community::class,
'event' => Event::class,
'currency' => Currency::class, 'currency' => Currency::class,
'payer_user' => User::class, 'payer_user' => User::class,
'payee_user' => User::class 'payee_user' => User::class
@ -20,6 +21,10 @@ class Transaction extends Model
private int $communityId; private int $communityId;
private ?Event $event = null;
private ?int $eventId = null;
private ?Currency $currency = null; private ?Currency $currency = null;
private int $currencyId; private int $currencyId;
@ -48,6 +53,16 @@ class Transaction extends Model
$this->communityId = $communityId; $this->communityId = $communityId;
} }
public function setEvent(?Event $event): void
{
$this->event = $event;
}
public function setEventId(?int $eventId): void
{
$this->eventId = $eventId;
}
public function setCurrency(Currency $currency): void public function setCurrency(Currency $currency): void
{ {
$this->currency = $currency; $this->currency = $currency;
@ -108,6 +123,16 @@ class Transaction extends Model
return $this->communityId; return $this->communityId;
} }
public function getEvent(): ?Event
{
return $this->event;
}
public function getEventId(): ?int
{
return $this->eventId;
}
public function getCurrency(): ?Currency public function getCurrency(): ?Currency
{ {
return $this->currency; return $this->currency;

View File

@ -0,0 +1,89 @@
<?php namespace RVR\Repository;
use Container;
use DateTime;
use Generator;
use RVR\PersistentData\Model\Community;
use RVR\PersistentData\Model\Event;
use RVR\PersistentData\Model\User;
use SokoWeb\Database\Query\Select;
class EventRepository
{
public function getById(int $id): ?Event
{
return Container::$persistentDataManager->selectFromDbById($id, Event::class);
}
public function getBySlug(string $slug): ?Event
{
return \Container::$persistentDataManager->selectFromDbBySlug($slug, Event::class);
}
public function getAllByCommunity(Community $community, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByCommunity($community);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations);
}
public function countAllByCommunity(Community $community): int
{
return $this->selectAllByCommunity($community)->count();
}
public function getUpcomingByCommunity(Community $community, DateTime $from, int $limit, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByCommunity($community);
$select->where('end', '>', $from->format('Y-m-d H:i:s'));
$select->orderBy('start', 'ASC');
$select->limit($limit, 0);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations);
}
public function getUpcomingByUser(User $user, DateTime $from, int $limit, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByUser($user);
$select->where('end', '>', $from->format('Y-m-d H:i:s'));
$select->orderBy('start', 'ASC');
$select->limit($limit, 0);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations);
}
public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByCommunity($community);
$select->orderBy('start', 'DESC');
$select->limit($limit, $start);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations);
}
public function searchByTitle(Community $community, string $title): Generator
{
$select = $this->selectAllByCommunity($community);
$select->where('title', 'LIKE', '%' . $title . '%');
$select->orderBy('start', 'DESC');
$select->limit(10);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class);
}
private function selectAllByCommunity(Community $community)
{
$select = new Select(Container::$dbConnection, Event::getTable());
$select->where('community_id', '=', $community->getId());
return $select;
}
private function selectAllByUser(User $user)
{
$select = new Select(Container::$dbConnection, Event::getTable());
$select->innerJoin('communities', ['communities', 'id'], '=', ['events', 'community_id']);
$select->innerJoin('community_members', ['communities', 'id'], '=', ['community_members', 'community_id']);
$select->where(['community_members', 'user_id'], '=', $user->getId());
return $select;
}
}

View File

@ -4,6 +4,7 @@ use Container;
use Generator; use Generator;
use RVR\PersistentData\Model\Community; use RVR\PersistentData\Model\Community;
use RVR\PersistentData\Model\Currency; use RVR\PersistentData\Model\Currency;
use RVR\PersistentData\Model\Event;
use RVR\PersistentData\Model\Transaction; use RVR\PersistentData\Model\Transaction;
use RVR\PersistentData\Model\User; use RVR\PersistentData\Model\User;
use SokoWeb\Database\Query\Select; use SokoWeb\Database\Query\Select;
@ -22,11 +23,23 @@ class TransactionRepository
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations); yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
} }
public function getAllByEvent(Event $event, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByEvent($event);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
}
public function countAllByCommunity(Community $community): int public function countAllByCommunity(Community $community): int
{ {
return $this->selectAllByCommunity($community)->count(); return $this->selectAllByCommunity($community)->count();
} }
public function countAllByEvent(Event $event): int
{
return $this->selectAllByEvent($event)->count();
}
public function isAnyForUser(User $user): bool public function isAnyForUser(User $user): bool
{ {
$select = new Select(Container::$dbConnection, Transaction::getTable()); $select = new Select(Container::$dbConnection, Transaction::getTable());
@ -55,8 +68,16 @@ class TransactionRepository
public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator
{ {
$select = new Select(Container::$dbConnection); $select = $this->selectAllByCommunity($community);
$select->where('community_id', '=', $community->getId()); $select->orderBy('time', 'DESC');
$select->limit($limit, $start);
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
}
public function getPagedByEvent(Event $event, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator
{
$select = $this->selectAllByEvent($event);
$select->orderBy('time', 'DESC'); $select->orderBy('time', 'DESC');
$select->limit($limit, $start); $select->limit($limit, $start);
@ -69,4 +90,11 @@ class TransactionRepository
$select->where('community_id', '=', $community->getId()); $select->where('community_id', '=', $community->getId());
return $select; return $select;
} }
private function selectAllByEvent(Event $event)
{
$select = new Select(Container::$dbConnection, Transaction::getTable());
$select->where('event_id', '=', $event->getId());
return $select;
}
} }

View File

@ -15,11 +15,15 @@
<h3 class="marginBottom">Upcoming events</h3> <h3 class="marginBottom">Upcoming events</h3>
<?php if (count($upcomingEvents) > 0): ?> <?php if (count($upcomingEvents) > 0): ?>
<?php foreach ($upcomingEvents as $event): ?> <?php foreach ($upcomingEvents as $event): ?>
<!-- todo --> <p>
<a href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>"><?= $event->getTitle() ?></a>
<span class="small"><?= $event->getStartDate()->format('Y-m-d') ?> <?= $event->getEndDate()->format('Y-m-d') ?></span>
</p>
<?php endforeach; ?> <?php endforeach; ?>
<?php else: ?> <?php else: ?>
<p>There is no upcoming event.</p> <p>There is no upcoming event.</p>
<?php endif; ?> <?php endif; ?>
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>">All events</a> | <a href="<?= Container::$routeCollection->getRoute('community.events.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New event</a></p>
</div> </div>
<div> <div>
<?php <?php
@ -27,7 +31,7 @@
$mainCurrencyRoundDigits = $community->getMainCurrency()->getRoundDigits(); $mainCurrencyRoundDigits = $community->getMainCurrency()->getRoundDigits();
?> ?>
<h3 class="marginBottom">Finances</h3> <h3 class="marginBottom">Finances</h3>
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Transactions</a> <p><a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Transactions</a> | <a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New transaction</a></p>
<table class="fullWidth marginTop"> <table class="fullWidth marginTop">
<tr> <tr>
<td class="bold">You owe</td> <td class="bold">You owe</td>

View File

@ -41,7 +41,7 @@
<tr> <tr>
<td> <td>
<form id="newMember" action="<?= Container::$routeCollection->getRoute('community.members.new-action')->generateLink(['communitySlug' => $community->getSlug()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="user_id"></form> <form id="newMember" action="<?= Container::$routeCollection->getRoute('community.members.new-action')->generateLink(['communitySlug' => $community->getSlug()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="user_id"></form>
<select type="text" form="newMember" name="user_id"> <select form="newMember" name="user_id" required></select>
</td> </td>
<td style="text-align: center;"> <td style="text-align: center;">
<input type="checkbox" form="newMember" name="owner" /> <input type="checkbox" form="newMember" name="owner" />

View File

@ -1,9 +1,17 @@
@css(/static/node_modules/tom-select/dist/css/tom-select.min.css)
@js(/static/node_modules/tom-select/dist/js/tom-select.base.min.js)
@js(js/communities/transaction.js)
@extends(templates/layout_normal) @extends(templates/layout_normal)
@section(main) @section(main)
<h2> <h2>
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> » <a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> »
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Transactions</a> » <a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Transactions</a> »
<?php if (isset($event)): ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Events</a> »
<a href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>"><?= $event->getTitle() ?></a> »
<?php endif; ?>
<?php if (isset($transaction)): ?> <?php if (isset($transaction)): ?>
Edit transaction Edit transaction
<?php else: ?> <?php else: ?>
@ -16,8 +24,15 @@
Container::$routeCollection->getRoute('community.transactions.edit-action')->generateLink(['communitySlug' => $community->getSlug(), 'transactionId' => $transaction->getId()]) : Container::$routeCollection->getRoute('community.transactions.edit-action')->generateLink(['communitySlug' => $community->getSlug(), 'transactionId' => $transaction->getId()]) :
Container::$routeCollection->getRoute('community.transactions.new-action')->generateLink(['communitySlug' => $community->getSlug()]); Container::$routeCollection->getRoute('community.transactions.new-action')->generateLink(['communitySlug' => $community->getSlug()]);
?> ?>
<form id="transactionForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug()]) ?>"> <form id="transactionForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug(), 'event' => isset($event) ? $event->getSlug() : null]) ?>">
<p class="formLabel">Payer</p> <p class="formLabel">Event</p>
<select name="event_id">
<option value="">[none]</option>
<?php if (isset($event)): ?>
<option value="<?= $event->getId() ?>" selected><?= $event->getTitle() ?></option>
<?php endif; ?>
</select>
<p class="formLabel marginTop">Payer</p>
<select class="big fullWidth" name="payer_user_id" required> <select class="big fullWidth" name="payer_user_id" required>
<option value="" hidden></option> <option value="" hidden></option>
<?php foreach ($members as $member): ?> <?php foreach ($members as $member): ?>
@ -57,3 +72,9 @@
<?php endif; ?> <?php endif; ?>
</div> </div>
@endsection @endsection
@section(pageScript)
<script>
var searchEventUrl = '<?= Container::$routeCollection->getRoute('community.events.search')->generateLink(['communitySlug' => $community->getSlug(), 'q' => 'QUERY']) ?>';
</script>
@endsection

View File

@ -3,10 +3,14 @@
@section(main) @section(main)
<h2> <h2>
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> » <a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> »
<?php if (isset($event)): ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Events</a> »
<a href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>"><?= $event->getTitle() ?></a> »
<?php endif; ?>
Transactions Transactions
</h2> </h2>
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New transaction</a></p> <p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug(), 'event' => isset($event) ? $event->getSlug() : null]) ?>">New transaction</a></p>
<?php if ($numberOfTransactions > 0): ?> <?php if ($numberOfTransactions > 0): ?>
<?php if ($pages > 1): ?> <?php if ($pages > 1): ?>
@ -29,6 +33,9 @@
<a class="block" href="<?= Container::$routeCollection->getRoute('community.transactions.edit')->generateLink(['communitySlug' => $community->getSlug(), 'transactionId' => $transaction->getId()]) ?>"> <a class="block" href="<?= Container::$routeCollection->getRoute('community.transactions.edit')->generateLink(['communitySlug' => $community->getSlug(), 'transactionId' => $transaction->getId()]) ?>">
<div class="box transaction"> <div class="box transaction">
<div> <div>
<?php if ($transaction->getEvent()): ?>
<p><span class="label"><?= $transaction->getEvent()->getTitle() ?></span></p>
<?php endif; ?>
<p style="font-weight: bold;"><?= $transaction->getDescription() ?></p> <p style="font-weight: bold;"><?= $transaction->getDescription() ?></p>
<p class="small"><?= $transaction->getPayerUser()->getDisplayName() ?> <i class="fa-solid fa-caret-right"></i> <?= $transaction->getPayeeUser() ? $transaction->getPayeeUser()->getDisplayName() : '[common]' ?></p> <p class="small"><?= $transaction->getPayerUser()->getDisplayName() ?> <i class="fa-solid fa-caret-right"></i> <?= $transaction->getPayeeUser() ? $transaction->getPayeeUser()->getDisplayName() : '[common]' ?></p>
<p class="small"><?= $transaction->getTimeDate()->format('Y-m-d H:i') ?></p> <p class="small"><?= $transaction->getTimeDate()->format('Y-m-d H:i') ?></p>
@ -59,7 +66,7 @@
</p> </p>
<?php endif; ?> <?php endif; ?>
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New transaction</a></p> <p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug(), 'event' => isset($event) ? $event->getSlug() : null]) ?>">New transaction</a></p>
<?php else: ?> <?php else: ?>
<div class="box"> <div class="box">
<p>There are no transactions yet.</p> <p>There are no transactions yet.</p>

27
views/events/event.php Normal file
View File

@ -0,0 +1,27 @@
@extends(templates/layout_normal)
@section(main)
<h2>
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> »
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Events</a> »
<?= $event->getTitle() ?>
<span class="small">[<a href="<?= Container::$routeCollection->getRoute('community.event.edit')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>">edit</a>]</span>
</h2>
<div class="gridContainer marginTop">
<div>
<?php
$mainCurrencyCode = $community->getMainCurrency()->getCode();
$mainCurrencyRoundDigits = $community->getMainCurrency()->getRoundDigits();
?>
<h3 class="marginBottom">Finances</h3>
<p><a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communitySlug' => $community->getSlug(), 'event' => $event->getSlug()]) ?>">Transactions</a> | <a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communitySlug' => $community->getSlug(), 'event' => $event->getSlug()]) ?>">New transaction</a></p>
<table class="fullWidth marginTop">
<tr>
<td class="bold">Total cost</td>
<td class="mono" style="text-align: right;"><?= number_format($totalCost, $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
</tr>
</table>
</div>
</div>
@endsection

View File

@ -0,0 +1,40 @@
@extends(templates/layout_normal)
@section(main)
<h2>
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> »
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>">Events</a> »
<?php if (isset($event)): ?>
<a href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>"><?= $event->getTitle() ?></a> » Edit event
<?php else: ?>
New event
<?php endif; ?>
</h2>
<div class="box compactBox">
<?php
$formAction = isset($event) ?
Container::$routeCollection->getRoute('community.event.edit-action')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) :
Container::$routeCollection->getRoute('community.events.new-action')->generateLink(['communitySlug' => $community->getSlug()]);
?>
<form id="eventForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="true">
<p class="formLabel">Title</p>
<input type="text" class="text big fullWidth" name="title" value="<?= isset($event) ? $event->getTitle() : '' ?>" required>
<p class="formLabel marginTop">Description</p>
<textarea class="text big fullWidth" name="description" rows="3"><?= isset($event) ? $event->getDescription() : '' ?></textarea>
<p class="formLabel marginTop">Start</p>
<input type="datetime-local" class="text big fullWidth" name="start" value="<?= isset($event) ? $event->getStartDate()->format('Y-m-d\TH:i') : '' ?>" required>
<p class="formLabel marginTop">End</p>
<input type="datetime-local" class="text big fullWidth" name="end" value="<?= isset($event) ? $event->getEndDate()->format('Y-m-d\TH:i') : '' ?>" required>
<p class="formError justify marginTop"></p>
<div class="right marginTop" style="font-size: 0;">
<button type="submit" name="submit_button"><?= isset($event) ? '<i class="fa-regular fa-floppy-disk"></i> Save' : '<i class="fa-regular fa-plus"></i> Create' ?></button>
<?php if (isset($event)): ?>
<button type="submit" form="deleteEvent" name="submit_button" data-confirmation="Are you sure you want to delete this event? All linked transactions will be unlinked!" class="red marginLeft"><i class="fa-regular fa-trash-can"></i> Delete</button>
<?php endif; ?>
</div>
</form>
<?php if (isset($event)): ?>
<form id="deleteEvent" action="<?= Container::$routeCollection->getRoute('community.event.delete-action')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug()]) ?>"></form>
<?php endif; ?>
</div>
@endsection

60
views/events/events.php Normal file
View File

@ -0,0 +1,60 @@
@extends(templates/layout_normal)
@section(main)
<h2>
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $community->getSlug()]) ?>"><?= $community->getName() ?></a> »
Events
</h2>
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.events.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New event</a></p>
<?php if ($numberOfEvents > 0): ?>
<?php if ($pages > 1): ?>
<p class="paginateContainer marginTop">
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => 0]) ?>">«</a>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => max(0, $currentPage - 1)]) ?>"></a>
<?php for ($i = 0; $i < $pages; $i++): ?>
<?php if ($currentPage == $i): ?>
<span class="selected"><?= $i + 1 ?></span>
<?php else: ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
<?php endif; ?>
<?php endfor; ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => min($pages - 1, $currentPage + 1)]) ?>"></a>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => $pages - 1]) ?>">»</a>
</p>
<?php endif; ?>
<?php foreach ($events as $event): ?>
<a class="block" href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $community->getSlug(), 'eventSlug' => $event->getSlug()]) ?>">
<div class="box">
<p style="font-weight: bold;"><?= $event->getTitle() ?></p>
<p class="small"><?= $event->getStartDate()->format('Y-m-d') ?> <?= $event->getEndDate()->format('Y-m-d') ?></p>
<p class="small"><?= $event->getDescription() ?></p>
</div>
</a>
<?php endforeach; ?>
<?php if ($pages > 1): ?>
<p class="paginateContainer marginTop">
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => 0]) ?>">«</a>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => max(0, $currentPage - 1)]) ?>"></a>
<?php for ($i = 0; $i < $pages; $i++): ?>
<?php if ($currentPage == $i): ?>
<span class="selected"><?= $i + 1 ?></span>
<?php else: ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
<?php endif; ?>
<?php endfor; ?>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => min($pages - 1, $currentPage + 1)]) ?>"></a>
<a href="<?= Container::$routeCollection->getRoute('community.events')->generateLink(['communitySlug' => $community->getSlug(), 'page' => $pages - 1]) ?>">»</a>
</p>
<?php endif; ?>
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.events.new')->generateLink(['communitySlug' => $community->getSlug()]) ?>">New event</a></p>
<?php else: ?>
<div class="box">
<p>There are no events yet.</p>
</div>
<?php endif; ?>
@endsection

View File

@ -19,7 +19,13 @@
<h3 class="marginBottom">Upcoming events</h3> <h3 class="marginBottom">Upcoming events</h3>
<?php if (count($upcomingEvents) > 0): ?> <?php if (count($upcomingEvents) > 0): ?>
<?php foreach ($upcomingEvents as $event): ?> <?php foreach ($upcomingEvents as $event): ?>
<!-- todo --> <p>
<a href="<?= Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $event->getCommunity()->getSlug(), 'eventSlug' => $event->getSlug()]) ?>"><?= $event->getTitle() ?></a>
<span class="small">
(<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communitySlug' => $event->getCommunity()->getSlug()]) ?>"><?= $event->getCommunity()->getName() ?></a>)
<?= $event->getStartDate()->format('Y-m-d') ?> <?= $event->getEndDate()->format('Y-m-d') ?>
</span>
</p>
<?php endforeach; ?> <?php endforeach; ?>
<?php else: ?> <?php else: ?>
<p>There is no upcoming event.</p> <p>There is no upcoming event.</p>

View File

@ -5,7 +5,7 @@
<meta name="description" content="<?= $_ENV['APP_NAME'] ?>"> <meta name="description" content="<?= $_ENV['APP_NAME'] ?>">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#212f4d"> <meta name="theme-color" content="#212f4d">
<title><?= $_ENV['APP_NAME'] ?></title> <title><?= isset($title) ? $title . ' ' : '' ?><?= $_ENV['APP_NAME'] ?></title>
<?php if (preg_match('/^(http(s)?:)?\/\//', $_ENV['STATIC_ROOT']) === 1): ?> <?php if (preg_match('/^(http(s)?:)?\/\//', $_ENV['STATIC_ROOT']) === 1): ?>
<link href="<?= $_ENV['STATIC_ROOT'] ?>" rel="preconnect"> <link href="<?= $_ENV['STATIC_ROOT'] ?>" rel="preconnect">
<?php endif; ?> <?php endif; ?>

13
web.php
View File

@ -13,6 +13,7 @@ use RVR\Controller\UserController;
use RVR\Controller\UserSearchController; use RVR\Controller\UserSearchController;
use RVR\Controller\CommunityController; use RVR\Controller\CommunityController;
use RVR\Controller\TransactionController; use RVR\Controller\TransactionController;
use RVR\Controller\EventController;
use RVR\Repository\UserRepository; use RVR\Repository\UserRepository;
require 'app.php'; require 'app.php';
@ -91,6 +92,18 @@ Container::$routeCollection->group('communities', function (RouteCollection $rou
$routeCollection->post('community.transactions.edit-action', '{transactionId}', [TransactionController::class, 'saveTransaction']); $routeCollection->post('community.transactions.edit-action', '{transactionId}', [TransactionController::class, 'saveTransaction']);
$routeCollection->post('community.transactions.delete-action', '{transactionId}/delete', [TransactionController::class, 'deleteTransaction']); $routeCollection->post('community.transactions.delete-action', '{transactionId}/delete', [TransactionController::class, 'deleteTransaction']);
}); });
$routeCollection->group('events', function (RouteCollection $routeCollection) {
$routeCollection->get('community.events', '', [EventController::class, 'getEvents']);
$routeCollection->get('community.events.new', 'new', [EventController::class, 'getEventEdit']);
$routeCollection->post('community.events.new-action', 'new', [EventController::class, 'saveEvent']);
$routeCollection->get('community.events.search', 'search', [EventController::class, 'searchEvent']);
$routeCollection->group('{eventSlug}', function (RouteCollection $routeCollection) {
$routeCollection->get('community.event', '', [EventController::class, 'getEvent']);
$routeCollection->get('community.event.edit', 'edit', [EventController::class, 'getEventEdit']);
$routeCollection->post('community.event.edit-action', 'edit', [EventController::class, 'saveEvent']);
$routeCollection->post('community.event.delete-action', 'delete', [EventController::class, 'deleteEvent']);
});
});
}); });
}); });