fixed several page and teacher page creation
This commit is contained in:
@@ -0,0 +1,410 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../config/api_config.dart';
|
||||
|
||||
/// Un partecipante a una classe (vista insegnante).
|
||||
class TeacherParticipant {
|
||||
final int bookingId;
|
||||
final String fullName;
|
||||
final bool isLost;
|
||||
|
||||
const TeacherParticipant({
|
||||
required this.bookingId,
|
||||
required this.fullName,
|
||||
required this.isLost,
|
||||
});
|
||||
|
||||
factory TeacherParticipant.fromMap(Map<String, dynamic> m) =>
|
||||
TeacherParticipant(
|
||||
bookingId: (m['booking_id'] as num?)?.toInt() ?? 0,
|
||||
fullName: (m['full_name'] ?? '').toString().trim(),
|
||||
isLost: m['is_lost'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Una classe programmata con i suoi partecipanti.
|
||||
class TeacherClass {
|
||||
final int scheduleId;
|
||||
final int serviceId;
|
||||
final String className;
|
||||
final String? colorClass;
|
||||
final String dateTime; // "YYYY-MM-DD HH:MM:SS"
|
||||
final int maxCapacity;
|
||||
final int bookedCount;
|
||||
final bool isFull;
|
||||
final List<TeacherParticipant> participants;
|
||||
|
||||
const TeacherClass({
|
||||
required this.scheduleId,
|
||||
required this.serviceId,
|
||||
required this.className,
|
||||
required this.colorClass,
|
||||
required this.dateTime,
|
||||
required this.maxCapacity,
|
||||
required this.bookedCount,
|
||||
required this.isFull,
|
||||
required this.participants,
|
||||
});
|
||||
|
||||
factory TeacherClass.fromMap(Map<String, dynamic> m) => TeacherClass(
|
||||
scheduleId: (m['schedule_id'] as num?)?.toInt() ?? 0,
|
||||
serviceId: (m['service_id'] as num?)?.toInt() ?? 0,
|
||||
className: (m['class_name'] ?? '').toString(),
|
||||
colorClass: (m['color_class'])?.toString(),
|
||||
dateTime: (m['date_time'] ?? '').toString(),
|
||||
maxCapacity: (m['max_capacity'] as num?)?.toInt() ?? 0,
|
||||
bookedCount: (m['booked_count'] as num?)?.toInt() ?? 0,
|
||||
isFull: m['is_full'] == true,
|
||||
participants: ((m['participants'] as List?) ?? [])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map(TeacherParticipant.fromMap)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Utente trovato nella ricerca (per aggiungi partecipante).
|
||||
class TeacherUser {
|
||||
final int id;
|
||||
final String firstName;
|
||||
final String lastName;
|
||||
final String email;
|
||||
|
||||
const TeacherUser({
|
||||
required this.id,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.email,
|
||||
});
|
||||
|
||||
String get fullName => '$firstName $lastName'.trim();
|
||||
|
||||
factory TeacherUser.fromMap(Map<String, dynamic> m) => TeacherUser(
|
||||
id: (m['id'] as num?)?.toInt() ?? 0,
|
||||
firstName: (m['first_name'] ?? '').toString(),
|
||||
lastName: (m['last_name'] ?? '').toString(),
|
||||
email: (m['email'] ?? '').toString(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Ordine/pacchetto di un utente con residui.
|
||||
class TeacherOrder {
|
||||
final int idOrderBook;
|
||||
final String serviceName;
|
||||
final int tickets;
|
||||
final int used;
|
||||
final int remaining;
|
||||
final String? expireOn;
|
||||
|
||||
const TeacherOrder({
|
||||
required this.idOrderBook,
|
||||
required this.serviceName,
|
||||
required this.tickets,
|
||||
required this.used,
|
||||
required this.remaining,
|
||||
required this.expireOn,
|
||||
});
|
||||
|
||||
factory TeacherOrder.fromMap(Map<String, dynamic> m) => TeacherOrder(
|
||||
idOrderBook: (m['idorderbook'] as num?)?.toInt() ?? 0,
|
||||
serviceName: (m['service_name'] ?? '').toString(),
|
||||
tickets: (m['tickets'] as num?)?.toInt() ?? 0,
|
||||
used: (m['used'] as num?)?.toInt() ?? 0,
|
||||
remaining: (m['remaining'] as num?)?.toInt() ?? 0,
|
||||
expireOn: (m['expireon'])?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
class TeacherApi {
|
||||
static Map<String, String> _headers(String token) => {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
};
|
||||
|
||||
/// Classi di un mese (YYYY-MM) con partecipanti. Solo staff lato server.
|
||||
static Future<List<TeacherClass>> fetchClasses({
|
||||
required String token,
|
||||
required String month,
|
||||
}) async {
|
||||
final query = month.isNotEmpty ? '?month=$month' : '';
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_classes.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 classi');
|
||||
}
|
||||
|
||||
return ((data['classes'] as List?) ?? [])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map(TeacherClass.fromMap)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Segna una prenotazione come persa (lost=true) o la ripristina (false).
|
||||
static Future<void> setLost({
|
||||
required String token,
|
||||
required int bookingId,
|
||||
required bool lost,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_mark_lost.php',
|
||||
);
|
||||
|
||||
final res = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: 'booking_id=$bookingId&lost=${lost ? 'Y' : 'N'}',
|
||||
);
|
||||
|
||||
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 aggiornamento',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Elimina una prenotazione (libera il ticket all'allievo).
|
||||
static Future<void> deleteBooking({
|
||||
required String token,
|
||||
required int bookingId,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_delete_booking.php',
|
||||
);
|
||||
|
||||
final res = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: 'booking_id=$bookingId',
|
||||
);
|
||||
|
||||
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 eliminazione',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Classi destinazione per riprogrammare una prenotazione
|
||||
/// (future, non piene, escluse quelle dove è già iscritto).
|
||||
static Future<List<TeacherClass>> availableClasses({
|
||||
required String token,
|
||||
required int bookingId,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_available_classes.php?booking_id=$bookingId',
|
||||
);
|
||||
|
||||
final res = await http.get(
|
||||
uri,
|
||||
headers: {'Accept': 'application/json', 'Authorization': 'Bearer $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 classi');
|
||||
}
|
||||
|
||||
return ((data['classes'] as List?) ?? [])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map(TeacherClass.fromMap)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Esegue la riprogrammazione su una nuova classe.
|
||||
static Future<void> reprogram({
|
||||
required String token,
|
||||
required int bookingId,
|
||||
required int scheduleId,
|
||||
required bool countAsReprogram,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_reprogram.php',
|
||||
);
|
||||
|
||||
final res = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body:
|
||||
'booking_id=$bookingId&schedule_id=$scheduleId'
|
||||
'&is_reprogrammed=${countAsReprogram ? 'Y' : 'N'}',
|
||||
);
|
||||
|
||||
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 riprogrammazione',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ricerca utenti per nome/cognome (min 2 caratteri in uno dei due).
|
||||
static Future<List<TeacherUser>> searchUsers({
|
||||
required String token,
|
||||
required String first,
|
||||
required String last,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_search_users.php'
|
||||
'?first=${Uri.encodeQueryComponent(first)}'
|
||||
'&last=${Uri.encodeQueryComponent(last)}',
|
||||
);
|
||||
|
||||
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 ricerca');
|
||||
}
|
||||
|
||||
return ((data['results'] as List?) ?? [])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map(TeacherUser.fromMap)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Ordini con residui di un utente.
|
||||
static Future<List<TeacherOrder>> userOrders({
|
||||
required String token,
|
||||
required int userId,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_user_orders.php?user_id=$userId',
|
||||
);
|
||||
|
||||
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 ordini');
|
||||
}
|
||||
|
||||
return ((data['orders'] as List?) ?? [])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map(TeacherOrder.fromMap)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Aggiunge un partecipante a una classe.
|
||||
/// [mode] = 'scala' | 'omaggio' | 'nuovo'.
|
||||
/// Per utente esistente passare [userId] > 0; per nuovo utente passare
|
||||
/// [userId] = 0 con name/surname (email opzionale).
|
||||
static Future<void> addBooking({
|
||||
required String token,
|
||||
required int scheduleId,
|
||||
required int serviceId,
|
||||
required String bookingStart,
|
||||
required String mode,
|
||||
int userId = 0,
|
||||
String name = '',
|
||||
String surname = '',
|
||||
String email = '',
|
||||
int? orderId,
|
||||
String? newExpiry,
|
||||
}) async {
|
||||
final uri = Uri.parse(
|
||||
'${ApiConfig.laravelApiBase}/api_teacher_add_booking.php',
|
||||
);
|
||||
|
||||
final body = <String, String>{
|
||||
'idserviceschedule': '$scheduleId',
|
||||
'idservice': '$serviceId',
|
||||
'bookingstart': bookingStart,
|
||||
'bkmode': mode,
|
||||
'user_id': '$userId',
|
||||
'name': name,
|
||||
'surname': surname,
|
||||
'email': email,
|
||||
if (orderId != null) 'idorder': '$orderId',
|
||||
if (newExpiry != null) 'new_expiry': newExpiry,
|
||||
};
|
||||
|
||||
final res = await http.post(
|
||||
uri,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: body.entries
|
||||
.map(
|
||||
(e) =>
|
||||
'${Uri.encodeQueryComponent(e.key)}='
|
||||
'${Uri.encodeQueryComponent(e.value)}',
|
||||
)
|
||||
.join('&'),
|
||||
);
|
||||
|
||||
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 inserimento');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user