184 lines
5.1 KiB
Dart
184 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../../../../api/connect/rmv/rmv_models.dart';
|
||
import '../../../../api/errors/error_mapper.dart';
|
||
import '../../../../extensions/date_time.dart';
|
||
import '../../../../state/app/modules/rmv/repository/rmv_repository.dart';
|
||
import '../../../../widget/app_progress_indicator.dart';
|
||
import '../../../../widget/details_bottom_sheet.dart';
|
||
|
||
class DisruptionsView extends StatefulWidget {
|
||
const DisruptionsView({super.key});
|
||
|
||
@override
|
||
State<DisruptionsView> createState() => _DisruptionsViewState();
|
||
}
|
||
|
||
class _DisruptionsViewState extends State<DisruptionsView> {
|
||
final RmvRepository _repo = RmvRepository();
|
||
List<HimMessage>? _items;
|
||
bool _loading = true;
|
||
Object? _error;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_load();
|
||
}
|
||
|
||
Future<void> _load() async {
|
||
setState(() {
|
||
_loading = true;
|
||
_error = null;
|
||
});
|
||
try {
|
||
final r = await _repo.disruptions();
|
||
if (!mounted) return;
|
||
r.sort((a, b) => (b.priority ?? 0).compareTo(a.priority ?? 0));
|
||
setState(() {
|
||
_items = r;
|
||
_loading = false;
|
||
});
|
||
} catch (e) {
|
||
if (!mounted) return;
|
||
setState(() {
|
||
_error = e;
|
||
_loading = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) => Scaffold(
|
||
appBar: AppBar(title: const Text('Störungsmeldungen')),
|
||
body: _body(),
|
||
);
|
||
|
||
Widget _body() {
|
||
if (_loading) return const Center(child: AppProgressIndicator.large());
|
||
final err = _error;
|
||
if (err != null) {
|
||
return Center(
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(24),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text(
|
||
errorToUserMessage(err),
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||
),
|
||
const SizedBox(height: 12),
|
||
FilledButton.icon(
|
||
icon: const Icon(Icons.refresh),
|
||
onPressed: _load,
|
||
label: const Text('Erneut versuchen'),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
final list = _items ?? const <HimMessage>[];
|
||
if (list.isEmpty) {
|
||
return RefreshIndicator(
|
||
onRefresh: _load,
|
||
child: ListView(
|
||
physics: const AlwaysScrollableScrollPhysics(),
|
||
children: const [
|
||
Padding(
|
||
padding: EdgeInsets.symmetric(vertical: 80, horizontal: 24),
|
||
child: Center(child: Text('Keine aktiven Meldungen.')),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
return RefreshIndicator(
|
||
onRefresh: _load,
|
||
child: ListView.separated(
|
||
itemCount: list.length,
|
||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||
itemBuilder: (_, i) => _tile(list[i]),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _tile(HimMessage msg) => ListTile(
|
||
leading: Icon(
|
||
Icons.warning_amber_outlined,
|
||
color: _priorityColor(msg.priority),
|
||
),
|
||
title: Text(
|
||
msg.head ?? msg.lead ?? msg.text ?? 'Meldung',
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
subtitle: msg.lead == null
|
||
? null
|
||
: Text(msg.lead!, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: () => _showDetails(msg),
|
||
);
|
||
|
||
Color _priorityColor(int? priority) {
|
||
if (priority == null) return Colors.orange;
|
||
if (priority >= 100) return Colors.red;
|
||
if (priority >= 50) return Colors.orange;
|
||
return Colors.amber;
|
||
}
|
||
|
||
void _showDetails(HimMessage msg) {
|
||
showDetailsBottomSheet(
|
||
context,
|
||
header: Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 4, 16, 12),
|
||
child: Text(
|
||
msg.head ?? 'Störungsmeldung',
|
||
style: Theme.of(context).textTheme.titleLarge,
|
||
),
|
||
),
|
||
children: (ctx) => [
|
||
if (msg.lead != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.short_text),
|
||
title: Text(msg.lead!),
|
||
),
|
||
if (msg.text != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.notes),
|
||
title: Text(msg.text!),
|
||
),
|
||
if (msg.category != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.category_outlined),
|
||
title: Text(msg.category!),
|
||
),
|
||
if (msg.company != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.business_outlined),
|
||
title: Text(msg.company!),
|
||
),
|
||
if (msg.startValidity != null || msg.endValidity != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.event_outlined),
|
||
title: Text(_validityRange(msg)),
|
||
),
|
||
if (msg.modified != null)
|
||
ListTile(
|
||
leading: const Icon(Icons.update_outlined),
|
||
title: Text('Aktualisiert: ${msg.modified!.formatDateTime()}'),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
String _validityRange(HimMessage msg) {
|
||
final start = msg.startValidity?.formatDateTime();
|
||
final end = msg.endValidity?.formatDateTime();
|
||
if (start != null && end != null) return '$start – $end';
|
||
return start ?? end ?? '';
|
||
}
|
||
}
|