fixed pages

This commit is contained in:
2026-09-04 08:15:25 +02:00
parent c80535eb97
commit a1167a2d03
14 changed files with 363 additions and 174 deletions
+199 -53
View File
@@ -27,8 +27,9 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
// copia locale modificabile dei partecipanti (per aggiornare la UI)
late List<TeacherParticipant> _participants;
int _busyBookingId =
0; // booking in aggiornamento (per disabilitare il tasto)
int _busyBookingId = 0; // booking in aggiornamento
bool _changed =
false; // true se ci sono state modifiche (per refresh lista)/ booking in aggiornamento (per disabilitare il tasto)
@override
void initState() {
@@ -100,6 +101,7 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
);
});
}
_changed = true;
_snack(markLost ? 'Segnata come persa.' : 'Ripristinata.');
} catch (e) {
_snack('$e');
@@ -143,6 +145,7 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
setState(() {
_participants.removeWhere((x) => x.bookingId == p.bookingId);
});
_changed = true;
_snack('Prenotazione eliminata.');
} catch (e) {
_snack('$e');
@@ -151,6 +154,129 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
}
}
Future<void> _confirmPending(TeacherParticipant p) async {
setState(() => _busyBookingId = p.bookingId);
try {
await TeacherApi.confirmPending(
token: widget.token,
bookingId: p.bookingId,
);
// da pending a confermato: aggiorna lo stato locale
final idx = _participants.indexWhere((x) => x.bookingId == p.bookingId);
if (idx != -1) {
setState(() {
_participants[idx] = TeacherParticipant(
bookingId: p.bookingId,
fullName: p.fullName,
isLost: false,
isPending: false,
);
});
}
_changed = true;
_snack('${p.fullName} confermato.');
} catch (e) {
_snack('$e');
} finally {
if (mounted) setState(() => _busyBookingId = 0);
}
}
Widget _pendingTile(TeacherParticipant p) {
final busy = _busyBookingId == p.bookingId;
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xFFFFFBF0),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFF5E6B8)),
),
child: Row(
children: [
Container(
width: 42,
height: 42,
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Color(0xFFE0A800),
shape: BoxShape.circle,
),
child: Text(
_initials(p.fullName),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 15,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
p.fullName,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 15,
),
),
const SizedBox(height: 2),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: const Color(0xFFFFF4D6),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'In attesa',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w800,
color: Color(0xFF8A6D00),
),
),
),
],
),
),
if (busy)
const SizedBox(
width: 36,
height: 36,
child: Padding(
padding: EdgeInsets.all(8),
child: CircularProgressIndicator(strokeWidth: 2),
),
)
else ...[
IconButton(
onPressed: () => _confirmPending(p),
tooltip: 'Conferma',
icon: const Icon(
Icons.check_circle_outline_rounded,
color: Color(0xFF1A7F52),
),
),
IconButton(
onPressed: () => _deleteBooking(p),
tooltip: 'Rimuovi',
icon: const Icon(
Icons.delete_outline_rounded,
color: Colors.black45,
),
),
],
],
),
);
}
Future<void> _reprogram(TeacherParticipant p) async {
final done = await Navigator.push<bool>(
context,
@@ -173,63 +299,85 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
@override
Widget build(BuildContext context) {
final active = _participants.where((p) => !p.isLost).toList();
final lost = _participants.where((p) => p.isLost).toList();
final pending = _participants.where((p) => p.isPending).toList();
final active = _participants
.where((p) => !p.isPending && !p.isLost)
.toList();
final lost = _participants.where((p) => !p.isPending && p.isLost).toList();
final cls = widget.cls;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
title: const Text(
'Dettaglio classe',
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18),
return PopScope(
canPop: true,
onPopInvokedWithResult: (didPop, result) {
// quando si esce, se ci sono state modifiche la lista si aggiorna
},
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, _changed),
),
title: const Text(
'Dettaglio classe',
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18),
),
),
),
body: YogibookBackground(
child: SafeArea(
top: false,
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: [
_headerCard(),
const SizedBox(height: 18),
_sectionLabel('Iscritti (${active.length})'),
const SizedBox(height: 8),
if (active.isEmpty)
_emptyRow('Nessun iscritto attivo.')
else
...active.map((p) => _tile(p)),
if (lost.isNotEmpty) ...[
body: YogibookBackground(
child: SafeArea(
top: false,
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: [
_headerCard(),
const SizedBox(height: 18),
_sectionLabel('Lezioni perse (${lost.length})'),
if (pending.isNotEmpty) ...[
_sectionLabel('In attesa di conferma (${pending.length})'),
const SizedBox(height: 8),
...pending.map((p) => _pendingTile(p)),
const SizedBox(height: 18),
],
_sectionLabel('Iscritti (${active.length})'),
const SizedBox(height: 8),
...lost.map((p) => _tile(p)),
],
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kGreen,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
if (active.isEmpty)
_emptyRow('Nessun iscritto attivo.')
else
...active.map((p) => _tile(p)),
if (lost.isNotEmpty) ...[
const SizedBox(height: 18),
_sectionLabel('Lezioni perse (${lost.length})'),
const SizedBox(height: 8),
...lost.map((p) => _tile(p)),
],
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kGreen,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
),
icon: const Icon(Icons.person_add_alt_1, color: Colors.white),
label: const Text(
'Aggiungi partecipante',
style: TextStyle(
fontWeight: FontWeight.w900,
icon: const Icon(
Icons.person_add_alt_1,
color: Colors.white,
),
label: const Text(
'Aggiungi partecipante',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
),
),
onPressed: _openAddParticipant,
),
onPressed: _openAddParticipant,
),
),
],
],
),
),
),
),
@@ -245,10 +393,8 @@ class _TeacherClassDetailPageState extends State<TeacherClassDetailPage> {
),
);
if (added == true && mounted) {
_snack(
'Partecipante aggiunto. Aggiorna il pannello per vedere la lista.',
);
// torna al pannello classi, che ricaricando mostrerà i dati aggiornati
_changed = true;
_snack('Partecipante aggiunto.');
Navigator.pop(context, true);
}
}