Files
yogibook_aury_app/lib/models/lesson.dart
T
2026-08-11 07:54:22 +02:00

317 lines
8.9 KiB
Dart

/// Modello delle lezioni per la scuola YogaSoul.
///
/// Rispecchia il JSON prodotto da `public/api/my_lessons.php`.
/// Nota: questa app è single-school, quindi niente concetti multiscuola
/// (wallet/entries/recoveries/level) che non esistono nel DB YogaSoul.
class LessonsResponse {
final String month; // "YYYY-MM"
final LessonsSummary summary;
final List<Lesson> lessons;
LessonsResponse({
required this.month,
required this.summary,
required this.lessons,
});
factory LessonsResponse.fromJson(Map<String, dynamic> j) {
final rawLessons = (j['lessons'] as List?) ?? const [];
return LessonsResponse(
month: (j['month'] ?? '').toString(),
summary: LessonsSummary.fromJson(
(j['summary'] as Map?)?.cast<String, dynamic>() ?? const {},
),
lessons: rawLessons
.map((e) => Lesson.fromJson((e as Map).cast<String, dynamic>()))
.toList(),
);
}
}
/// I conteggi della fascia riepilogo (le "card pastello" della webapp).
class LessonsSummary {
final int purchased; // acquistate
final int practiced; // praticate
final int booked; // prenotate
final int pending; // da confermare
final int toSchedule; // da programmare
final int lost; // perse
LessonsSummary({
required this.purchased,
required this.practiced,
required this.booked,
required this.pending,
required this.toSchedule,
required this.lost,
});
factory LessonsSummary.fromJson(Map<String, dynamic> j) {
int n(dynamic v) => (v as num?)?.toInt() ?? 0;
return LessonsSummary(
purchased: n(j['purchased']),
practiced: n(j['practiced']),
booked: n(j['booked']),
pending: n(j['pending']),
toSchedule: n(j['to_schedule']),
lost: n(j['lost']),
);
}
}
class Lesson {
final int bookingId;
final int serviceId;
final String status;
final String datetime; // "YYYY-MM-DD HH:MM:SS"
final String date; // "YYYY-MM-DD"
final String time; // "HH:MM"
final String className;
final String color; // es. "#1ebf73"
final String location;
final String? expireOn; // "YYYY-MM-DD" o null
final bool lostLesson;
final bool canReschedule;
final bool canDelete;
Lesson({
required this.bookingId,
required this.serviceId,
required this.status,
required this.datetime,
required this.date,
required this.time,
required this.className,
required this.color,
required this.location,
required this.expireOn,
required this.lostLesson,
required this.canReschedule,
required this.canDelete,
});
factory Lesson.fromJson(Map<String, dynamic> j) {
return Lesson(
bookingId: (j['booking_id'] as num?)?.toInt() ?? 0,
serviceId: (j['service_id'] as num?)?.toInt() ?? 0,
status: (j['status'] ?? '').toString(),
datetime: (j['datetime'] ?? '').toString(),
date: (j['date'] ?? '').toString(),
time: (j['time'] ?? '').toString(),
className: (j['class_name'] ?? '').toString(),
color: (j['color'] ?? '#1ebf73').toString(),
location: (j['location'] ?? '').toString(),
expireOn: j['expire_on']?.toString(),
lostLesson: j['lost_lesson'] == true,
canReschedule: j['can_reschedule'] == true,
canDelete: j['can_delete'] == true,
);
}
}
/// Uno slot disponibile per riprogrammare o prenotare (da available_slots.php).
class AvailableSlot {
final int scheduleId;
final int serviceId;
final String datetime;
final String date;
final String time;
final String className;
final String color;
final String location;
final int maxCapacity;
final int bookedCount;
final int freePlaces;
final bool alreadyBooked;
final bool bookable;
AvailableSlot({
required this.scheduleId,
required this.serviceId,
required this.datetime,
required this.date,
required this.time,
required this.className,
required this.color,
required this.location,
required this.maxCapacity,
required this.bookedCount,
required this.freePlaces,
required this.alreadyBooked,
required this.bookable,
});
factory AvailableSlot.fromJson(Map<String, dynamic> j) {
int n(dynamic v) => (v as num?)?.toInt() ?? 0;
return AvailableSlot(
scheduleId: n(j['schedule_id']),
serviceId: n(j['service_id']),
datetime: (j['datetime'] ?? '').toString(),
date: (j['date'] ?? '').toString(),
time: (j['time'] ?? '').toString(),
className: (j['class_name'] ?? '').toString(),
color: (j['color'] ?? '#1ebf73').toString(),
location: (j['location'] ?? '').toString(),
maxCapacity: n(j['max_capacity']),
bookedCount: n(j['booked_count']),
freePlaces: n(j['free_places']),
alreadyBooked: j['already_booked'] == true,
bookable: j['bookable'] == true,
);
}
}
/// Un ordine/pacchetto con ticket residui (da orders.php).
class OrderPackage {
final int orderId;
final int serviceId;
final String serviceName;
final int tickets;
final int used;
final int remaining;
final String? expireOn;
final bool isExpired;
final bool bookable;
OrderPackage({
required this.orderId,
required this.serviceId,
required this.serviceName,
required this.tickets,
required this.used,
required this.remaining,
required this.expireOn,
required this.isExpired,
required this.bookable,
});
factory OrderPackage.fromJson(Map<String, dynamic> j) {
int n(dynamic v) => (v as num?)?.toInt() ?? 0;
return OrderPackage(
orderId: n(j['order_id']),
serviceId: n(j['service_id']),
serviceName: (j['service_name'] ?? '').toString(),
tickets: n(j['tickets']),
used: n(j['used']),
remaining: n(j['remaining']),
expireOn: j['expire_on']?.toString(),
isExpired: j['is_expired'] == true,
bookable: j['bookable'] == true,
);
}
}
/// Una lezione dentro un ordine (da orders_detail.php).
class OrderLesson {
final int bookingId;
final String? datetime;
final String className;
final String status; // completed / booked / lost / expired / pending
final bool isReprogrammed;
OrderLesson({
required this.bookingId,
required this.datetime,
required this.className,
required this.status,
required this.isReprogrammed,
});
factory OrderLesson.fromJson(Map<String, dynamic> j) {
return OrderLesson(
bookingId: (j['booking_id'] as num?)?.toInt() ?? 0,
datetime: j['datetime']?.toString(),
className: (j['class_name'] ?? '').toString(),
status: (j['status'] ?? '').toString(),
isReprogrammed: j['is_reprogrammed'] == true,
);
}
}
/// Un ordine completo con conteggi e lezioni (da orders_detail.php).
class OrderDetail {
final int orderId;
final int? orderNumber;
final int serviceId;
final String serviceName;
final String day;
final String time;
final String? orderDate;
final String? firstLessonDate;
final String? expireOn;
final bool isExpired;
final int maxReschedule;
final int reprogrammed;
final int tickets;
final int total;
final int completed;
final int lost;
final int expired;
final int booked;
final int pending;
final int toSchedule;
final List<OrderLesson> lessons;
OrderDetail({
required this.orderId,
required this.orderNumber,
required this.serviceId,
required this.serviceName,
required this.day,
required this.time,
required this.orderDate,
required this.firstLessonDate,
required this.expireOn,
required this.isExpired,
required this.maxReschedule,
required this.reprogrammed,
required this.tickets,
required this.total,
required this.completed,
required this.lost,
required this.expired,
required this.booked,
required this.pending,
required this.toSchedule,
required this.lessons,
});
factory OrderDetail.fromJson(Map<String, dynamic> j) {
int n(dynamic v) => (v as num?)?.toInt() ?? 0;
final s = (j['summary'] as Map?)?.cast<String, dynamic>() ?? const {};
final rawLessons = (j['lessons'] as List?) ?? const [];
return OrderDetail(
orderId: n(j['order_id']),
orderNumber: (j['order_number'] as num?)?.toInt(),
serviceId: n(j['service_id']),
serviceName: (j['service_name'] ?? '').toString(),
day: (j['day'] ?? '').toString(),
time: (j['time'] ?? '').toString(),
orderDate: j['order_date']?.toString(),
firstLessonDate: j['first_lesson_date']?.toString(),
expireOn: j['expire_on']?.toString(),
isExpired: j['is_expired'] == true,
maxReschedule: n(j['max_reschedule']),
reprogrammed: n(j['reprogrammed']),
tickets: n(j['tickets']),
total: n(s['total']),
completed: n(s['completed']),
lost: n(s['lost']),
expired: n(s['expired']),
booked: n(s['booked']),
pending: n(s['pending']),
toSchedule: n(s['to_schedule']),
lessons: rawLessons
.map((e) => OrderLesson.fromJson((e as Map).cast<String, dynamic>()))
.toList(),
);
}
}