api start

This commit is contained in:
2026-07-27 10:10:32 +02:00
parent 49a9d2a2a6
commit 9c623f96c0
2 changed files with 129 additions and 30 deletions
+90 -30
View File
@@ -1,52 +1,112 @@
/// 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 String status;
final String date; // YYYY-MM-DD
final String startTime;
final String endTime;
final String? roomName;
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? level;
final String color; // es. "#1ebf73"
final String location;
final int availableEntries;
final int availableRecoveries;
final String? expireOn; // "YYYY-MM-DD" o null
final bool lostLesson;
final bool canModify;
final bool canReschedule;
final bool canDelete;
Lesson({
required this.bookingId,
required this.status,
required this.datetime,
required this.date,
required this.startTime,
required this.endTime,
required this.roomName,
required this.time,
required this.className,
required this.level,
required this.availableEntries,
required this.availableRecoveries,
required this.canModify,
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) {
final session = (j['session'] as Map<String, dynamic>? ?? {});
final cls = (j['class'] as Map<String, dynamic>? ?? {});
final wallet = (j['wallet'] as Map<String, dynamic>? ?? {});
return Lesson(
bookingId: (j['booking_id'] as num).toInt(),
bookingId: (j['booking_id'] as num?)?.toInt() ?? 0,
status: (j['status'] ?? '').toString(),
date: (session['date'] ?? '').toString(),
startTime: (session['start_time'] ?? '').toString(),
endTime: (session['end_time'] ?? '').toString(),
roomName: session['room_name']?.toString(),
className: (cls['name'] ?? '').toString(),
level: cls['level']?.toString(),
availableEntries: (wallet['available_entries'] as num?)?.toInt() ?? 0,
availableRecoveries:
(wallet['available_recoveries'] as num?)?.toInt() ?? 0,
canModify: (j['can_modify'] == true),
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,
);
}
}
+39
View File
@@ -0,0 +1,39 @@
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.phpApiBase}/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('Risposta non valida dal server (${res.statusCode})');
}
if (res.statusCode != 200 || data['success'] != true) {
throw Exception(data['error'] ?? data['message'] ?? 'Errore lezioni');
}
return LessonsResponse.fromJson(data);
}
}