false, 'message' => 'Missing required parameter: school_id']); exit; } $month = $_GET['month'] ?? (new DateTimeImmutable('now', new DateTimeZone('Europe/Rome')))->format('Y-m'); if (!preg_match('/^\d{4}-\d{2}$/', (string)$month)) { http_response_code(422); echo json_encode(['success' => false, 'message' => 'Invalid month format. Use YYYY-MM.']); exit; } $tz = new DateTimeZone('Europe/Rome'); $monthDate = DateTimeImmutable::createFromFormat('Y-m', $month, $tz) ?: new DateTimeImmutable('now', $tz); $startOfMonth = $monthDate->format('Y-m-01'); // inclusive $endOfMonth = $monthDate->modify('first day of next month')->format('Y-m-d'); // exclusive $prevMonth = $monthDate->modify('-1 month')->format('Y-m'); $nextMonth = $monthDate->modify('+1 month')->format('Y-m'); // --- Authorization: user must belong to that school --- // Adjust if your authorization logic differs. $stmt = $pdo->prepare(" SELECT 1 FROM user_schools WHERE user_id = ? AND school_id = ? AND status = 'active' LIMIT 1 "); $stmt->execute([$iduserlogin, $school_id]); if (!$stmt->fetchColumn()) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Forbidden: user not allowed for this school.']); exit; } // --- School info (same as your page) --- $stmt = $pdo->prepare(" SELECT id, name, address_street, address_city, address_province FROM schools WHERE id = ? LIMIT 1 "); $stmt->execute([$school_id]); $school = $stmt->fetch(PDO::FETCH_ASSOC); if (!$school) { http_response_code(404); echo json_encode(['success' => false, 'message' => 'School not found.']); exit; } // --- Main query (ported from your PHP page) --- $stmt = $pdo->prepare(" SELECT sb.id as booking_id, sb.status, sb.order_id, cs.id as session_id, cs.session_date, cs.start_time, cs.end_time, cs.room_name, c.name as class_name, ct.level, o.available_entries, o.available_recoveries FROM session_bookings sb JOIN class_sessions cs ON sb.session_id = cs.id JOIN class_types ct ON cs.class_type_id = ct.id JOIN classes c ON ct.class_id = c.id JOIN orders o ON sb.order_id = o.id WHERE sb.user_id = ? AND cs.school_id = ? AND cs.session_date >= ? AND cs.session_date < ? ORDER BY cs.session_date ASC, cs.start_time ASC "); $stmt->execute([$iduserlogin, $school_id, $startOfMonth, $endOfMonth]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); // Better 24h rule: compare against actual lesson start datetime $now = new DateTimeImmutable('now', $tz); $limit = $now->modify('+24 hours'); $lessons = []; foreach ($rows as $r) { $startDt = DateTimeImmutable::createFromFormat( 'Y-m-d H:i:s', $r['session_date'] . ' ' . $r['start_time'], $tz ); if (!$startDt) { // fallback if start_time is H:i $startDt = DateTimeImmutable::createFromFormat( 'Y-m-d H:i', $r['session_date'] . ' ' . substr((string)$r['start_time'], 0, 5), $tz ); } $canModify = false; if ($r['status'] === 'booked' && $startDt instanceof DateTimeImmutable) { $canModify = ($startDt > $limit); } $lessons[] = [ 'booking_id' => (int)$r['booking_id'], 'status' => (string)$r['status'], 'order_id' => (int)$r['order_id'], 'session' => [ 'session_id' => (int)$r['session_id'], 'date' => (string)$r['session_date'], 'start_time' => (string)$r['start_time'], 'end_time' => (string)$r['end_time'], 'room_name' => $r['room_name'] !== null ? (string)$r['room_name'] : null, 'start_datetime_iso' => $startDt ? $startDt->format(DateTimeInterface::ATOM) : null, ], 'class' => [ 'name' => (string)$r['class_name'], 'level' => ($r['level'] ?? null) ? (string)$r['level'] : null, ], 'wallet' => [ 'available_entries' => (int)($r['available_entries'] ?? 0), 'available_recoveries' => (int)($r['available_recoveries'] ?? 0), ], 'can_reschedule' => $canModify, 'can_cancel' => $canModify, 'can_modify' => $canModify, ]; } echo json_encode([ 'success' => true, 'month' => $monthDate->format('Y-m'), 'prev_month' => $prevMonth, 'next_month' => $nextMonth, 'school' => [ 'id' => (int)$school['id'], 'name' => (string)$school['name'], 'address_street' => $school['address_street'] ?? null, 'address_city' => $school['address_city'] ?? null, 'address_province' => $school['address_province'] ?? null, 'address_full' => trim( ($school['address_street'] ?? '') . ', ' . ($school['address_city'] ?? '') . ' ' . ($school['address_province'] ?? '') ), ], 'lessons' => $lessons ], JSON_UNESCAPED_UNICODE); } catch (Throwable $e) { http_response_code(500); echo json_encode([ 'success' => false, 'message' => 'Server error.', 'error' => $e->getMessage() ]); }