role_id; if (!in_array($roleId, [1, 3], true)) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Forbidden']); exit; } // --- Mese richiesto (default: corrente) --- $month = isset($_GET['month']) ? preg_replace('/[^0-9\-]/', '', $_GET['month']) : date('Y-m'); if (!preg_match('/^\d{4}-\d{2}$/', $month)) { $month = date('Y-m'); } $monthStart = $month . '-01'; if (strtotime($monthStart) === false) { $monthStart = date('Y-m-01'); } $monthEnd = date('Y-m-t', strtotime($monthStart)); // --- Classi del mese --- $sqlSchedule = "SELECT serviceschedule.idserviceschedule, serviceschedule.idservice, serviceschedule.dateschedule, service.servicename, service.colorclass, service.maxcapacity FROM serviceschedule LEFT JOIN service ON serviceschedule.idservice = service.idservice WHERE serviceschedule.dateschedule BETWEEN :start AND DATE_ADD(:end, INTERVAL 1 DAY) ORDER BY serviceschedule.dateschedule"; $stmt = $db->prepare($sqlSchedule); $stmt->execute([':start' => $monthStart, ':end' => $monthEnd]); $scheduleRows = $stmt->fetchAll(PDO::FETCH_ASSOC); // --- Bookings di tutte le classi del mese (una query sola) --- $bookingsBySchedule = []; $scheduleIds = array_filter(array_map(fn($r) => (int) $r['idserviceschedule'], $scheduleRows)); if (!empty($scheduleIds)) { $ph = implode(',', array_fill(0, count($scheduleIds), '?')); $sqlBookings = "SELECT bookingclass.idbookingclass, bookingclass.idserviceschedule, bookingclass.lostlesson, bookingclass.status, auth_users.first_name, auth_users.last_name, auth_users.avatar FROM bookingclass LEFT JOIN auth_users ON bookingclass.iduser = auth_users.id WHERE bookingclass.idserviceschedule IN ($ph) AND bookingclass.status IN ('booked', 'pending') ORDER BY auth_users.last_name, auth_users.first_name"; $stmtB = $db->prepare($sqlBookings); $stmtB->execute(array_values($scheduleIds)); foreach ($stmtB->fetchAll(PDO::FETCH_ASSOC) as $b) { $sid = (int) $b['idserviceschedule']; $first = trim((string) ($b['first_name'] ?? '')); $last = trim((string) ($b['last_name'] ?? '')); $avatar = trim((string) ($b['avatar'] ?? '')); $bookingsBySchedule[$sid][] = [ 'booking_id' => (int) $b['idbookingclass'], 'first_name' => $first, 'last_name' => $last, 'full_name' => trim($first . ' ' . $last), 'is_lost' => ($b['lostlesson'] === 'Y'), 'is_pending' => ($b['status'] === 'pending'), // avatar: solo il filename; l'app compone l'URL se serve 'avatar' => $avatar !== '' ? $avatar : null, ]; } } // --- Composizione risposta --- $classes = []; foreach ($scheduleRows as $sch) { $sid = (int) $sch['idserviceschedule']; $participants = $bookingsBySchedule[$sid] ?? []; // contatore: solo confermati (booked, perse incluse); i pending NON contano $count = 0; foreach ($participants as $pp) { if (empty($pp['is_pending'])) { $count++; } } $maxcap = (int) $sch['maxcapacity']; $classes[] = [ 'schedule_id' => $sid, 'service_id' => (int) $sch['idservice'], 'class_name' => (string) ($sch['servicename'] ?? ''), 'color_class' => $sch['colorclass'] ?: null, 'date_time' => $sch['dateschedule'], // "YYYY-MM-DD HH:MM:SS" 'max_capacity' => $maxcap, 'booked_count' => $count, 'is_full' => $maxcap > 0 && $count >= $maxcap, 'participants' => $participants, ]; } echo json_encode([ 'success' => true, 'month' => $month, 'classes' => $classes, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);