Files
2026-08-11 07:54:22 +02:00

209 lines
6.6 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../config/api_config.dart';
import '../models/lesson.dart';
/// Service per le lezioni della scuola YogaSoul.
/// Chiama `public/api/my_lessons.php` (phpApiBase), stesso pattern di SettingsApi.
class LessonsApi {
static Map<String, String> _headers(String token) => {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
};
/// Carica le lezioni del mese indicato.
/// [month] deve essere nel formato "YYYY-MM". Se null, il server usa il mese corrente.
static Future<LessonsResponse> fetchMyLessons({
required String token,
String? month,
}) async {
final query = (month != null && month.isNotEmpty) ? '?month=$month' : '';
final uri = Uri.parse('${ApiConfig.laravelApiBase}/my_lessons.php$query');
final res = await http.get(uri, headers: _headers(token));
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('RAW(${res.statusCode}): ${res.body}');
}
if (res.statusCode != 200 || data['success'] != true) {
throw Exception(data['error'] ?? data['message'] ?? 'Errore lezioni');
}
return LessonsResponse.fromJson(data);
}
/// Cancella una prenotazione dell'utente.
static Future<bool> cancelLesson({
required String token,
required int bookingId,
}) async {
final uri = Uri.parse('${ApiConfig.laravelApiBase}/cancel_lesson.php');
final res = await http.post(
uri,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({'booking_id': bookingId}),
);
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode == 200 && data['success'] == true) {
return true;
}
throw Exception(data['error'] ?? data['message'] ?? 'Errore cancellazione');
}
/// Elenca gli slot disponibili per riprogrammare/prenotare.
static Future<List<AvailableSlot>> fetchAvailableSlots({
required String token,
required int serviceId,
String? month,
}) async {
final monthQuery = (month != null && month.isNotEmpty)
? '&month=$month'
: '';
final uri = Uri.parse(
'${ApiConfig.laravelApiBase}/available_slots.php?service_id=$serviceId$monthQuery',
);
final res = await http.get(uri, headers: _headers(token));
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode != 200 || data['success'] != true) {
throw Exception(data['error'] ?? data['message'] ?? 'Errore slot');
}
final raw = (data['slots'] as List?) ?? const [];
return raw
.map((e) => AvailableSlot.fromJson((e as Map).cast<String, dynamic>()))
.toList();
}
/// Esegue la riprogrammazione di una lezione su un nuovo slot.
static Future<String> reschedule({
required String token,
required int bookingId,
required int newScheduleId,
}) async {
final uri = Uri.parse('${ApiConfig.laravelApiBase}/reschedule.php');
final res = await http.post(
uri,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({
'booking_id': bookingId,
'new_schedule_id': newScheduleId,
}),
);
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode == 200 && data['success'] == true) {
return (data['message'] ?? 'Riprogrammazione richiesta').toString();
}
throw Exception(
data['error'] ?? data['message'] ?? 'Errore riprogrammazione',
);
}
/// Elenca gli ordini/pacchetti dell'utente con residui.
static Future<List<OrderPackage>> fetchOrders({required String token}) async {
final uri = Uri.parse('${ApiConfig.laravelApiBase}/orders.php');
final res = await http.get(uri, headers: _headers(token));
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode != 200 || data['success'] != true) {
throw Exception(data['error'] ?? data['message'] ?? 'Errore ordini');
}
final raw = (data['orders'] as List?) ?? const [];
return raw
.map((e) => OrderPackage.fromJson((e as Map).cast<String, dynamic>()))
.toList();
}
/// Prenota una nuova lezione da un ticket residuo.
static Future<String> bookFromTicket({
required String token,
required int orderId,
required int newScheduleId,
}) async {
final uri = Uri.parse('${ApiConfig.laravelApiBase}/book_from_ticket.php');
final res = await http.post(
uri,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({'order_id': orderId, 'new_schedule_id': newScheduleId}),
);
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode == 200 && data['success'] == true) {
return (data['message'] ?? 'Prenotazione richiesta').toString();
}
throw Exception(data['error'] ?? data['message'] ?? 'Errore prenotazione');
}
/// Elenca gli ordini dettagliati (con conteggi e lezioni).
static Future<List<OrderDetail>> fetchOrdersDetail({
required String token,
}) async {
final uri = Uri.parse('${ApiConfig.laravelApiBase}/orders_detail.php');
final res = await http.get(uri, headers: _headers(token));
Map<String, dynamic> data;
try {
data = jsonDecode(res.body) as Map<String, dynamic>;
} catch (_) {
throw Exception('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode != 200 || data['success'] != true) {
throw Exception(data['error'] ?? data['message'] ?? 'Errore ordini');
}
final raw = (data['orders'] as List?) ?? const [];
return raw
.map((e) => OrderDetail.fromJson((e as Map).cast<String, dynamic>()))
.toList();
}
}