feat: redesign tickets main view and basket view with improved styles and layout

- Updated SCSS for tickets main view and basket view, enhancing visual design and responsiveness.
- Added new calendar functionality in the admin panel for managing ticket availability.
- Implemented error handling for ticket availability in the basket view.
- Introduced a new calendar availability table in the database for tracking ticket dates.
- Added a new link to the calendar in the logged-in user navigation.
- Improved ticket selection logic to disable unavailable tickets in the main view.
- Created guidelines for code standards in AGENTS.md.
This commit is contained in:
2026-03-06 01:06:43 +01:00
parent 238c9ecaed
commit 38f5babde3
14 changed files with 2218 additions and 261 deletions

View File

@@ -1,6 +1,203 @@
<?php
namespace factory;
class Tickets {
private static $calendarTableReady = false;
private static $allowedDateCache = [];
static public function getCalendarDefinitions()
{
return [
'park-rozrywki' => [
'label' => 'Park rozrywki i dinozaurów + bilety prezentowe',
'ticket_ids' => [
'plac-zabaw-ulgowy',
'plac-zabaw-normalny',
'gift-plac-zabaw-ulgowy',
'gift-plac-zabaw-normalny',
],
],
'park-wodny' => [
'label' => 'Park wodny',
'ticket_ids' => [
'park-wodny-ulgowy',
'park-wodny-normalny',
],
],
'all-open' => [
'label' => 'All Open + bilety prezentowe',
'ticket_ids' => [
'bilet-all-open-ulgowy',
'bilet-all-open-normalny',
'gift-bilet-all-open-ulgowy',
'gift-bilet-all-open-normalny',
],
],
];
}
static public function ensureCalendarTable()
{
global $mdb;
if (self::$calendarTableReady) {
return;
}
$sql = "CREATE TABLE IF NOT EXISTS ticket_calendar_availability (
ticket_group VARCHAR(64) NOT NULL,
available_date DATE NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (ticket_group, available_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8";
$mdb->pdo->exec($sql);
self::$calendarTableReady = true;
}
static public function normalizeDateFromPicker($date)
{
$date = trim((string) $date);
if ($date === '') {
return null;
}
$formats = ['d-m-Y', 'Y-m-d'];
foreach ($formats as $format) {
$parsedDate = \DateTime::createFromFormat($format, $date);
if ($parsedDate && $parsedDate->format($format) === $date) {
return $parsedDate->format('Y-m-d');
}
}
return null;
}
static public function getCalendarGroupForTicket($ticketId)
{
foreach (self::getCalendarDefinitions() as $groupKey => $group) {
if (in_array($ticketId, $group['ticket_ids'], true)) {
return $groupKey;
}
}
return null;
}
static public function getEnabledDatesByGroup($groupKey)
{
global $mdb;
self::ensureCalendarTable();
$definitions = self::getCalendarDefinitions();
if (!isset($definitions[$groupKey])) {
return [];
}
$statement = $mdb->pdo->prepare(
"SELECT available_date
FROM ticket_calendar_availability
WHERE ticket_group = :ticket_group
ORDER BY available_date ASC"
);
$statement->execute([':ticket_group' => $groupKey]);
$rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
if (!is_array($rows)) {
return [];
}
return array_values(array_filter(array_map(static function ($row) {
return $row['available_date'] ?? null;
}, $rows)));
}
static public function saveEnabledDatesForGroup($groupKey, array $dates)
{
global $mdb;
self::ensureCalendarTable();
$definitions = self::getCalendarDefinitions();
if (!isset($definitions[$groupKey])) {
return false;
}
$normalizedDates = [];
foreach ($dates as $date) {
$normalizedDate = self::normalizeDateFromPicker($date);
if (!$normalizedDate) {
continue;
}
$normalizedDates[$normalizedDate] = $normalizedDate;
}
$normalizedDates = array_values($normalizedDates);
$mdb->pdo->beginTransaction();
try {
$deleteStatement = $mdb->pdo->prepare(
"DELETE FROM ticket_calendar_availability WHERE ticket_group = :ticket_group"
);
$deleteStatement->execute([':ticket_group' => $groupKey]);
if (!empty($normalizedDates)) {
$insertStatement = $mdb->pdo->prepare(
"INSERT INTO ticket_calendar_availability (ticket_group, available_date)
VALUES (:ticket_group, :available_date)"
);
foreach ($normalizedDates as $date) {
$insertStatement->execute([
':ticket_group' => $groupKey,
':available_date' => $date,
]);
}
}
$mdb->pdo->commit();
} catch (\Exception $exception) {
$mdb->pdo->rollBack();
return false;
}
self::$allowedDateCache = [];
return true;
}
static public function canBuyTicketOnDate($ticketId, $date)
{
$groupKey = self::getCalendarGroupForTicket($ticketId);
if (!$groupKey) {
return true;
}
$normalizedDate = self::normalizeDateFromPicker($date);
if (!$normalizedDate) {
return false;
}
$cacheKey = $groupKey . '|' . $normalizedDate;
if (array_key_exists($cacheKey, self::$allowedDateCache)) {
return self::$allowedDateCache[$cacheKey];
}
$enabledDates = self::getEnabledDatesByGroup($groupKey);
$isAllowed = in_array($normalizedDate, $enabledDates, true);
self::$allowedDateCache[$cacheKey] = $isAllowed;
return $isAllowed;
}
static public function getTicketAvailabilityForDate(array $ticketIds, $date)
{
$availability = [];
foreach ($ticketIds as $ticketId) {
$availability[$ticketId] = self::canBuyTicketOnDate($ticketId, $date);
}
return $availability;
}
static public function recalculate_ticket_protection( $basket ) {
global $settings;
@@ -110,4 +307,4 @@ class Tickets {
return false;
}
}
}