import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/app_theme.dart'; import '../../../models/job.dart'; import '../../../models/patient.dart'; import '../jobs/clinic_jobs_repository.dart'; import 'clinic_patients_repository.dart'; class ClinicPatientDetailScreen extends ConsumerStatefulWidget { const ClinicPatientDetailScreen({super.key, required this.patientId}); final String patientId; @override ConsumerState createState() => _ClinicPatientDetailScreenState(); } class _ClinicPatientDetailScreenState extends ConsumerState { late Future _future; late Future> _jobsFuture; bool _editMode = false; bool _isSaving = false; final _patientCodeController = TextEditingController(); final _firstNameController = TextEditingController(); final _lastNameController = TextEditingController(); final _phoneController = TextEditingController(); final _notesController = TextEditingController(); String? _birthDate; final _formKey = GlobalKey(); @override void initState() { super.initState(); _load(); } @override void dispose() { _patientCodeController.dispose(); _firstNameController.dispose(); _lastNameController.dispose(); _phoneController.dispose(); _notesController.dispose(); super.dispose(); } void _load() { setState(() { _future = ClinicPatientsRepository.instance .getPatient(widget.patientId) .then((p) { _populateControllers(p); return p; }); _jobsFuture = ClinicJobsRepository.instance .listJobsByPatient(widget.patientId); }); } void _populateControllers(Patient p) { _patientCodeController.text = p.patientCode; _firstNameController.text = p.firstName ?? ''; _lastNameController.text = p.lastName ?? ''; _phoneController.text = p.phone ?? ''; _notesController.text = p.notes ?? ''; _birthDate = p.birthDate; } Future _pickBirthDate() async { DateTime initial = DateTime(1990); if (_birthDate != null) { try { initial = DateTime.parse(_birthDate!); } catch (_) {} } final picked = await showDatePicker( context: context, initialDate: initial, firstDate: DateTime(1900), lastDate: DateTime.now(), ); if (picked != null) { setState(() { _birthDate = picked.toIso8601String().split('T').first; }); } } Future _save() async { if (!_formKey.currentState!.validate()) return; setState(() => _isSaving = true); try { final patch = { 'patient_code': _patientCodeController.text.trim(), 'first_name': _firstNameController.text.trim().isNotEmpty ? _firstNameController.text.trim() : null, 'last_name': _lastNameController.text.trim().isNotEmpty ? _lastNameController.text.trim() : null, 'phone': _phoneController.text.trim().isNotEmpty ? _phoneController.text.trim() : null, 'birth_date': _birthDate, 'notes': _notesController.text.trim().isNotEmpty ? _notesController.text.trim() : null, }; final updated = await ClinicPatientsRepository.instance .updatePatient(widget.patientId, patch); _populateControllers(updated); setState(() { _editMode = false; _future = Future.value(updated); }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Hasta bilgileri güncellendi.')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Hata: $e')), ); } } finally { if (mounted) setState(() => _isSaving = false); } } Future _delete() async { // Check for existing jobs first List? jobs; try { jobs = await ClinicJobsRepository.instance .listJobsByPatient(widget.patientId, limit: 1); } catch (_) { jobs = null; } if (!mounted) return; final hasJobs = (jobs?.isNotEmpty) ?? false; final confirmed = await showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Hastayı Sil'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Bu hastayı silmek istediğinizden emin misiniz?'), if (hasJobs) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: AppColors.cancelledBg, borderRadius: BorderRadius.circular(8), ), child: const Row( children: [ Icon(Icons.warning_amber_rounded, color: AppColors.cancelled, size: 18), SizedBox(width: 8), Expanded( child: Text( 'Bu hastaya ait işler bulunmaktadır. Hasta silinirse bu bağlantı kopar.', style: TextStyle( fontSize: 13, color: AppColors.cancelled), ), ), ], ), ), ], ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Vazgeç')), FilledButton( onPressed: () => Navigator.pop(context, true), style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled), child: const Text('Sil'), ), ], ), ); if (confirmed != true || !mounted) return; try { await ClinicPatientsRepository.instance.deletePatient(widget.patientId); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Hasta silindi.')), ); Navigator.of(context).pop(true); // signal that a delete happened } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Silme hatası: $e')), ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Hasta Detayı'), actions: [ if (!_editMode) ...[ IconButton( icon: const Icon(Icons.edit_outlined), tooltip: 'Düzenle', onPressed: () => setState(() => _editMode = true), ), IconButton( icon: const Icon(Icons.delete_outline, color: AppColors.cancelled), tooltip: 'Sil', onPressed: _delete, ), ] else ...[ if (_isSaving) const Padding( padding: EdgeInsets.all(12), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ) else ...[ TextButton( onPressed: () { setState(() => _editMode = false); _future.then(_populateControllers); }, child: const Text('İptal'), ), FilledButton( onPressed: _save, child: const Text('Kaydet'), ), const SizedBox(width: 8), ], ], ], ), body: FutureBuilder( future: _future, builder: (ctx, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Hata: ${snap.error}'), const SizedBox(height: 12), FilledButton( onPressed: _load, child: const Text('Tekrar Dene'), ), ], ), ); } if (_editMode) { return _EditForm( formKey: _formKey, patientCodeController: _patientCodeController, firstNameController: _firstNameController, lastNameController: _lastNameController, phoneController: _phoneController, notesController: _notesController, birthDate: _birthDate, onPickBirthDate: _pickBirthDate, ); } final patient = snap.data!; return _PatientView(patient: patient, jobsFuture: _jobsFuture); }, ), ); } } // ── View ─────────────────────────────────────────────────────────────────── class _PatientView extends StatelessWidget { const _PatientView({required this.patient, required this.jobsFuture}); final Patient patient; final Future> jobsFuture; @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.all(16), children: [ // Avatar + name header Center( child: Column( children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: AppColors.inProgressBg, borderRadius: BorderRadius.circular(20)), child: Center( child: Text( patient.displayName.isNotEmpty ? patient.displayName[0].toUpperCase() : '?', style: const TextStyle( fontSize: 28, fontWeight: FontWeight.w800, color: AppColors.inProgress), ), ), ), const SizedBox(height: 12), Text( patient.displayName, style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), ), Text( patient.patientCode, style: const TextStyle( fontSize: 13, color: AppColors.textSecondary), ), ], ), ), const SizedBox(height: 24), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 12, offset: const Offset(0, 4)) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _DetailRow(label: 'Hasta Kodu', value: patient.patientCode), if (patient.firstName != null) _DetailRow(label: 'Ad', value: patient.firstName!), if (patient.lastName != null) _DetailRow(label: 'Soyad', value: patient.lastName!), if (patient.phone != null && patient.phone!.isNotEmpty) _DetailRow(label: 'Telefon', value: patient.phone!), if (patient.birthDate != null && patient.birthDate!.isNotEmpty) _DetailRow( label: 'Doğum Tarihi', value: patient.birthDate!), if (patient.notes != null && patient.notes!.isNotEmpty) _DetailRow(label: 'Notlar', value: patient.notes!), ], ), ), const SizedBox(height: 24), // Job history _JobHistory(jobsFuture: jobsFuture), const SizedBox(height: 24), ], ); } } // ── Job history ──────────────────────────────────────────────────────────── class _JobHistory extends StatelessWidget { const _JobHistory({required this.jobsFuture}); final Future> jobsFuture; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'İŞ GEÇMİŞİ', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.textSecondary, letterSpacing: 0.5, ), ), const SizedBox(height: 8), FutureBuilder>( future: jobsFuture, builder: (ctx, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) { return Text('Yüklenemedi: ${snap.error}', style: const TextStyle(color: AppColors.textSecondary)); } final jobs = snap.data ?? []; if (jobs.isEmpty) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), ), child: const Center( child: Text( 'Henüz iş kaydı yok.', style: TextStyle(color: AppColors.textSecondary), ), ), ); } return Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 12, offset: const Offset(0, 4)) ], ), child: Column( children: jobs.asMap().entries.map((entry) { final i = entry.key; final job = entry.value; final isLast = i == jobs.length - 1; return Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: _statusBg(job.status), borderRadius: BorderRadius.circular(10), ), child: Icon( _statusIcon(job.status), color: _statusColor(job.status), size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( job.patientCode, style: const TextStyle( fontWeight: FontWeight.w700, color: AppColors.textPrimary, fontSize: 14, ), ), Text( '${job.prostheticType.label} · ${_statusLabel(job.status)}', style: const TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), ], ), ), Text( _formatDate(job.dateCreated), style: const TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), ], ), ), if (!isLast) const Divider( height: 1, indent: 68, color: AppColors.border), ], ); }).toList(), ), ); }, ), ], ); } static Color _statusBg(JobStatus s) => switch (s) { JobStatus.delivered => AppColors.successBg, JobStatus.cancelled => AppColors.cancelledBg, JobStatus.inProgress => AppColors.inProgressBg, _ => AppColors.pendingBg, }; static Color _statusColor(JobStatus s) => switch (s) { JobStatus.delivered => AppColors.success, JobStatus.cancelled => AppColors.cancelled, JobStatus.inProgress => AppColors.inProgress, _ => AppColors.pending, }; static IconData _statusIcon(JobStatus s) => switch (s) { JobStatus.delivered => Icons.check_circle_outline, JobStatus.cancelled => Icons.cancel_outlined, JobStatus.inProgress => Icons.autorenew, _ => Icons.hourglass_empty_outlined, }; static String _statusLabel(JobStatus s) => switch (s) { JobStatus.pending => 'Bekliyor', JobStatus.inProgress => 'Üretimde', JobStatus.sent => 'Gönderildi', JobStatus.delivered => 'Teslim Edildi', JobStatus.cancelled => 'İptal', }; static String _formatDate(DateTime d) { return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; } } // ── Helpers ──────────────────────────────────────────────────────────────── class _DetailRow extends StatelessWidget { const _DetailRow({required this.label, required this.value}); final String label; final String value; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text( label, style: const TextStyle( fontSize: 13, color: AppColors.textSecondary), ), ), Expanded( child: Text( value, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.textPrimary), ), ), ], ), ); } } // ── Edit form ────────────────────────────────────────────────────────────── class _EditForm extends StatelessWidget { const _EditForm({ required this.formKey, required this.patientCodeController, required this.firstNameController, required this.lastNameController, required this.phoneController, required this.notesController, required this.birthDate, required this.onPickBirthDate, }); final GlobalKey formKey; final TextEditingController patientCodeController; final TextEditingController firstNameController; final TextEditingController lastNameController; final TextEditingController phoneController; final TextEditingController notesController; final String? birthDate; final VoidCallback onPickBirthDate; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: patientCodeController, decoration: const InputDecoration( labelText: 'Hasta Kodu *', border: OutlineInputBorder(), ), validator: (val) => (val == null || val.trim().isEmpty) ? 'Hasta kodu zorunludur' : null, ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextFormField( controller: firstNameController, decoration: const InputDecoration( labelText: 'Ad', border: OutlineInputBorder(), ), ), ), const SizedBox(width: 12), Expanded( child: TextFormField( controller: lastNameController, decoration: const InputDecoration( labelText: 'Soyad', border: OutlineInputBorder(), ), ), ), ], ), const SizedBox(height: 12), TextFormField( controller: phoneController, decoration: const InputDecoration( labelText: 'Telefon', border: OutlineInputBorder(), ), keyboardType: TextInputType.phone, ), const SizedBox(height: 12), InkWell( onTap: onPickBirthDate, child: InputDecorator( decoration: const InputDecoration( labelText: 'Doğum Tarihi', border: OutlineInputBorder(), suffixIcon: Icon(Icons.calendar_today), ), child: Text( birthDate ?? 'Tarih seçin', style: birthDate != null ? null : TextStyle( color: Theme.of(context) .colorScheme .onSurfaceVariant, ), ), ), ), const SizedBox(height: 12), TextFormField( controller: notesController, decoration: const InputDecoration( labelText: 'Notlar', border: OutlineInputBorder(), ), minLines: 3, maxLines: 5, ), ], ), ), ); } }