278 lines
7.9 KiB
Dart
278 lines
7.9 KiB
Dart
part of '../../app/app.dart';
|
|
|
|
class ExaminationDetailScreen extends StatefulWidget {
|
|
const ExaminationDetailScreen({
|
|
super.key,
|
|
required this.token,
|
|
required this.baseUrl,
|
|
required this.id,
|
|
});
|
|
|
|
final String token;
|
|
final String baseUrl;
|
|
final int id;
|
|
|
|
@override
|
|
State<ExaminationDetailScreen> createState() =>
|
|
_ExaminationDetailScreenState();
|
|
}
|
|
|
|
class _ExaminationDetailScreenState extends State<ExaminationDetailScreen> {
|
|
late final ApiClient _api;
|
|
late Future<Map<String, dynamic>> _future;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_api = ApiClient(baseUrl: widget.baseUrl, token: widget.token);
|
|
_future = _api.get('api/mobile/examinations/${widget.id}');
|
|
}
|
|
|
|
void _reload() {
|
|
setState(() {
|
|
_future = _api.get('api/mobile/examinations/${widget.id}');
|
|
});
|
|
}
|
|
|
|
Future<void> _launch(String? url) async {
|
|
if (url == null || url.isEmpty) {
|
|
return;
|
|
}
|
|
final uri = Uri.parse(url);
|
|
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Halaman tidak bisa dibuka.')),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _openExpertise() async {
|
|
await Navigator.of(context).push(
|
|
MaterialPageRoute(
|
|
builder: (_) => ExpertiseScreen(
|
|
token: widget.token,
|
|
baseUrl: widget.baseUrl,
|
|
id: widget.id,
|
|
),
|
|
),
|
|
);
|
|
_reload();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: FutureBuilder<Map<String, dynamic>>(
|
|
future: _future,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState != ConnectionState.done) {
|
|
return const LoadingScreen();
|
|
}
|
|
if (snapshot.hasError) {
|
|
return ErrorView(
|
|
message: _message(snapshot.error),
|
|
onRetry: _reload,
|
|
);
|
|
}
|
|
final data = snapshot.data!;
|
|
final item = asMap(data['item']);
|
|
return ListView(
|
|
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
|
|
children: [
|
|
SafeArea(
|
|
bottom: false,
|
|
child: _ExaminationScreenHeader(
|
|
title: 'Detail Pemeriksaan',
|
|
subtitle: item['nofoto']?.toString() ?? '-',
|
|
onRefresh: _reload,
|
|
),
|
|
),
|
|
const SizedBox(height: 22),
|
|
_ExaminationPatientCard(item: item),
|
|
const SizedBox(height: 14),
|
|
_ExaminationInfoCard(item: item),
|
|
const SizedBox(height: 14),
|
|
_ExaminationActionPanel(
|
|
onExpertise: _openExpertise,
|
|
onRefresh: _reload,
|
|
resultUrl: data['result_url']?.toString(),
|
|
onPreview: () => _launch(data['result_url']?.toString()),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ExaminationPatientCard extends StatelessWidget {
|
|
const _ExaminationPatientCard({required this.item});
|
|
|
|
final Map<String, dynamic> item;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(18),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
border: Border.all(color: const Color(0xFFE1E8F5)),
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFF2563EB).withValues(alpha: 0.05),
|
|
blurRadius: 18,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
item['nmpasien']?.toString() ?? '-',
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
color: const Color(0xFF080D3D),
|
|
fontWeight: FontWeight.w900,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
StatusPill(status: item['status']?.toString() ?? 'NEW'),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: [
|
|
BadgeLabel(
|
|
text: item['nofoto']?.toString() ?? '-',
|
|
color: const Color(0xFF0B7BFF),
|
|
),
|
|
BadgeLabel(
|
|
text: 'RM ${item['noregister'] ?? '-'}',
|
|
color: const Color(0xFF667095),
|
|
),
|
|
BadgeLabel(
|
|
text: item['nm_spesimen']?.toString() ?? '-',
|
|
color: const Color(0xFFFF6B00),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
item['reques']?.toString() ?? '-',
|
|
style: const TextStyle(
|
|
color: Color(0xFF47517A),
|
|
fontWeight: FontWeight.w800,
|
|
height: 1.35,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ExaminationInfoCard extends StatelessWidget {
|
|
const _ExaminationInfoCard({required this.item});
|
|
|
|
final Map<String, dynamic> item;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final rows = [
|
|
('Asal Pasien', item['asalpasien']),
|
|
('Ruangan', item['ruangan']),
|
|
('Dokter Pengirim', item['klinisi'] ?? item['nmdokter']),
|
|
('Tanggal Daftar', item['daftar']),
|
|
('Tanggal Sampel', item['tanggalsampel']),
|
|
('Cara Pengambilan', item['pengambilan']),
|
|
('Asal Pengambilan', item['asalpengirim']),
|
|
('Alamat', item['alamatpasien']),
|
|
];
|
|
return Container(
|
|
padding: const EdgeInsets.all(18),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
border: Border.all(color: const Color(0xFFE1E8F5)),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Informasi Pemeriksaan',
|
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
color: const Color(0xFF080D3D),
|
|
fontWeight: FontWeight.w900,
|
|
),
|
|
),
|
|
const SizedBox(height: 14),
|
|
...rows.map((row) => DetailRow(label: row.$1, value: row.$2)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ExaminationActionPanel extends StatelessWidget {
|
|
const _ExaminationActionPanel({
|
|
required this.onExpertise,
|
|
required this.onRefresh,
|
|
required this.resultUrl,
|
|
required this.onPreview,
|
|
});
|
|
|
|
final VoidCallback onExpertise;
|
|
final VoidCallback onRefresh;
|
|
final String? resultUrl;
|
|
final VoidCallback onPreview;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
border: Border.all(color: const Color(0xFFE1E8F5)),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
FilledButton.icon(
|
|
onPressed: onExpertise,
|
|
icon: const Icon(Icons.edit_document),
|
|
label: const Text('Expertise'),
|
|
),
|
|
const SizedBox(height: 10),
|
|
OutlinedButton.icon(
|
|
onPressed: onRefresh,
|
|
icon: const Icon(Icons.fact_check_outlined),
|
|
label: const Text('Cek Status'),
|
|
),
|
|
if (resultUrl?.isNotEmpty == true) ...[
|
|
const SizedBox(height: 10),
|
|
TextButton.icon(
|
|
onPressed: onPreview,
|
|
icon: const Icon(Icons.description_outlined),
|
|
label: const Text('Preview Hasil'),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|