import 'package:flutter/material.dart'; import '../../../core/theme/app_theme.dart'; import '../../../models/connection.dart'; import '../../../models/job.dart'; import 'connection_stats_repository.dart'; class ConnectionDetailScreen extends StatefulWidget { const ConnectionDetailScreen({ super.key, required this.connection, required this.labTenantId, }); final Connection connection; final String labTenantId; @override State createState() => _ConnectionDetailScreenState(); } class _ConnectionDetailScreenState extends State { late Future _future; @override void initState() { super.initState(); _load(); } void _load() { setState(() { _future = ConnectionStatsRepository.instance.fetchStats( labTenantId: widget.labTenantId, clinicTenantId: widget.connection.clinicTenantId, ); }); } @override Widget build(BuildContext context) { final conn = widget.connection; final clinicName = conn.clinicName ?? 'Klinik'; return Scaffold( backgroundColor: AppColors.background, body: CustomScrollView( slivers: [ SliverAppBar( pinned: true, expandedHeight: 140, backgroundColor: AppColors.primary, foregroundColor: Colors.white, flexibleSpace: FlexibleSpaceBar( background: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF0F172A), AppColors.primary], ), ), child: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(20, 48, 20, 16), child: Row( children: [ Container( width: 52, height: 52, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white.withValues(alpha: 0.2)), ), child: const Icon(Icons.local_hospital_outlined, color: Colors.white, size: 26), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( clinicName, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 3), _StatusBadge(status: conn.status), ], ), ), ], ), ), ), ), ), ), SliverToBoxAdapter( child: FutureBuilder( future: _future, builder: (ctx, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Padding( padding: EdgeInsets.symmetric(vertical: 80), child: Center(child: CircularProgressIndicator(color: AppColors.accent)), ); } if (snap.hasError) { return Padding( padding: const EdgeInsets.all(32), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.wifi_off_rounded, color: AppColors.cancelled, size: 40), const SizedBox(height: 12), Text('Hata: ${snap.error}', style: const TextStyle(color: AppColors.textSecondary), textAlign: TextAlign.center), const SizedBox(height: 12), FilledButton.icon( onPressed: _load, icon: const Icon(Icons.refresh_rounded, size: 16), label: const Text('Tekrar Dene'), ), ], ), ), ); } final stats = snap.data!; return _StatsBody(stats: stats); }, ), ), ], ), ); } } // ── Stats body ──────────────────────────────────────────────────────────────── class _StatsBody extends StatelessWidget { const _StatsBody({required this.stats}); final ConnectionStats stats; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // KPI row Row( children: [ Expanded(child: _KpiCard(label: 'Toplam İş', value: '${stats.totalJobs}', icon: Icons.work_outline_rounded, color: AppColors.accent)), const SizedBox(width: 10), Expanded(child: _KpiCard(label: 'Aktif', value: '${stats.activeJobs}', icon: Icons.pending_outlined, color: AppColors.inProgress)), const SizedBox(width: 10), Expanded(child: _KpiCard(label: 'Teslim', value: '${stats.deliveredJobs}', icon: Icons.check_circle_outline, color: AppColors.success)), ], ), const SizedBox(height: 10), Row( children: [ Expanded(child: _KpiCard(label: 'Bu Ay', value: '${stats.thisMonthJobs}', icon: Icons.calendar_month_outlined, color: AppColors.accent)), const SizedBox(width: 10), Expanded(child: _KpiCard(label: 'Geçen Ay', value: '${stats.lastMonthJobs}', icon: Icons.history_rounded, color: AppColors.textSecondary)), const SizedBox(width: 10), Expanded(child: _KpiCard(label: 'İptal', value: '${stats.cancelledJobs}', icon: Icons.cancel_outlined, color: AppColors.cancelled)), ], ), const SizedBox(height: 16), // Revenue _SectionTitle('Finans'), const SizedBox(height: 8), _RevenueCard(stats: stats), const SizedBox(height: 16), // Prosthetic type breakdown if (stats.byType.isNotEmpty) ...[ _SectionTitle('Ürün Tipi Dağılımı'), const SizedBox(height: 8), _TypeBreakdownCard(byType: stats.byType, total: stats.totalJobs), const SizedBox(height: 16), ], // Monthly trend _SectionTitle('Aylık Karşılaştırma'), const SizedBox(height: 8), _MonthCompareCard(stats: stats), const SizedBox(height: 16), // Recent jobs if (stats.recentJobs.isNotEmpty) ...[ _SectionTitle('Son İşler'), const SizedBox(height: 8), _RecentJobsCard(jobs: stats.recentJobs), const SizedBox(height: 16), ], const SizedBox(height: 32), ], ), ); } } // ── KPI card ────────────────────────────────────────────────────────────────── class _KpiCard extends StatelessWidget { const _KpiCard({required this.label, required this.value, required this.icon, required this.color}); final String label; final String value; final IconData icon; final Color color; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 6, offset: const Offset(0, 2))], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18, color: color), const SizedBox(height: 8), Text(value, style: TextStyle(fontSize: 22, fontWeight: FontWeight.w800, color: color)), const SizedBox(height: 2), Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textMuted, fontWeight: FontWeight.w500)), ], ), ); } } // ── Revenue card ────────────────────────────────────────────────────────────── class _RevenueCard extends StatelessWidget { const _RevenueCard({required this.stats}); final ConnectionStats stats; @override Widget build(BuildContext context) { final collected = stats.totalRevenue - stats.pendingRevenue; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), ), child: Column( children: [ _RevRow( label: 'Toplam Gelir', value: _fmt(stats.totalRevenue), color: AppColors.textPrimary, bold: true, ), const Divider(height: 20), _RevRow(label: 'Tahsil Edilen', value: _fmt(collected), color: AppColors.success), const SizedBox(height: 8), _RevRow(label: 'Bekleyen Alacak', value: _fmt(stats.pendingRevenue), color: AppColors.pending), ], ), ); } String _fmt(double v) => '${v.toStringAsFixed(0)} TL'; } class _RevRow extends StatelessWidget { const _RevRow({required this.label, required this.value, required this.color, this.bold = false}); final String label; final String value; final Color color; final bool bold; @override Widget build(BuildContext context) { final style = TextStyle( fontSize: bold ? 15 : 14, fontWeight: bold ? FontWeight.w700 : FontWeight.w500, color: color, ); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: style.copyWith(color: bold ? AppColors.textPrimary : AppColors.textSecondary)), Text(value, style: style), ], ); } } // ── Type breakdown ──────────────────────────────────────────────────────────── class _TypeBreakdownCard extends StatelessWidget { const _TypeBreakdownCard({required this.byType, required this.total}); final Map byType; final int total; @override Widget build(BuildContext context) { final sorted = byType.entries.toList()..sort((a, b) => b.value.compareTo(a.value)); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), ), child: Column( children: [ for (int i = 0; i < sorted.length; i++) ...[ if (i > 0) const SizedBox(height: 10), _TypeBar( label: _typeLabel(sorted[i].key), count: sorted[i].value, total: total, ), ], ], ), ); } String _typeLabel(String key) => switch (key) { 'metal_porselen' => 'Metal Porselen', 'zirkonyum' => 'Zirkonyum', 'implant_ustu_zirkonyum'=> 'İmplant Üstü Zirkonyum', 'gecici' => 'Geçici', 'e_max' => 'E-Max', 'tam_protez' => 'Tam Protez', 'parsiyel' => 'Parsiyel Protez', _ => 'Diğer', }; } class _TypeBar extends StatelessWidget { const _TypeBar({required this.label, required this.count, required this.total}); final String label; final int count; final int total; @override Widget build(BuildContext context) { final pct = total > 0 ? count / total : 0.0; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.textPrimary)), Text('$count adet · ${(pct * 100).toStringAsFixed(0)}%', style: const TextStyle(fontSize: 12, color: AppColors.textMuted)), ], ), const SizedBox(height: 5), ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: pct, minHeight: 7, backgroundColor: AppColors.border, valueColor: const AlwaysStoppedAnimation(AppColors.accent), ), ), ], ); } } // ── Monthly compare ─────────────────────────────────────────────────────────── class _MonthCompareCard extends StatelessWidget { const _MonthCompareCard({required this.stats}); final ConnectionStats stats; @override Widget build(BuildContext context) { final thisMonth = stats.thisMonthJobs; final lastMonth = stats.lastMonthJobs; final maxBar = (thisMonth > lastMonth ? thisMonth : lastMonth).clamp(1, 999); final trend = lastMonth == 0 ? null : ((thisMonth - lastMonth) / lastMonth * 100).toStringAsFixed(0); final isUp = thisMonth >= lastMonth; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (trend != null) ...[ Row( children: [ Icon( isUp ? Icons.trending_up_rounded : Icons.trending_down_rounded, size: 18, color: isUp ? AppColors.success : AppColors.cancelled, ), const SizedBox(width: 6), Text( isUp ? '+$trend% geçen aya göre' : '$trend% geçen aya göre', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: isUp ? AppColors.success : AppColors.cancelled, ), ), ], ), const SizedBox(height: 12), ], Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ _BarColumn(label: 'Geçen Ay', count: lastMonth, maxCount: maxBar, color: AppColors.border), const SizedBox(width: 16), _BarColumn(label: 'Bu Ay', count: thisMonth, maxCount: maxBar, color: AppColors.accent), ], ), ], ), ); } } class _BarColumn extends StatelessWidget { const _BarColumn({required this.label, required this.count, required this.maxCount, required this.color}); final String label; final int count; final int maxCount; final Color color; @override Widget build(BuildContext context) { final height = maxCount > 0 ? (count / maxCount * 80).clamp(4.0, 80.0) : 4.0; return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text('$count', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: color == AppColors.border ? AppColors.textSecondary : AppColors.accent)), const SizedBox(height: 4), Container( width: 40, height: height, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(4), ), ), const SizedBox(height: 4), Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textMuted)), ], ); } } // ── Recent jobs ─────────────────────────────────────────────────────────────── class _RecentJobsCard extends StatelessWidget { const _RecentJobsCard({required this.jobs}); final List jobs; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), ), child: Column( children: [ for (int i = 0; i < jobs.length; i++) ...[ if (i > 0) const Divider(height: 1), _RecentJobRow(job: jobs[i]), ], ], ), ); } } class _RecentJobRow extends StatelessWidget { const _RecentJobRow({required this.job}); final Job job; @override Widget build(BuildContext context) { final color = job.status == JobStatus.delivered ? AppColors.success : AppColors.inProgress; final bg = job.status == JobStatus.delivered ? AppColors.successBg : AppColors.inProgressBg; return Padding( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11), child: Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(10)), child: Center(child: Icon(Icons.assignment_outlined, size: 18, color: color)), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(job.patientCode, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), Text(job.prostheticType.label, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6)), child: Text(job.status.label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: color)), ), ], ), ); } } // ── Helpers ─────────────────────────────────────────────────────────────────── class _SectionTitle extends StatelessWidget { const _SectionTitle(this.text); final String text; @override Widget build(BuildContext context) { return Text(text, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.textMuted, letterSpacing: 0.5)); } } class _StatusBadge extends StatelessWidget { const _StatusBadge({required this.status}); final ConnectionStatus status; @override Widget build(BuildContext context) { final (label, color) = switch (status) { ConnectionStatus.approved => ('Onaylı', AppColors.success), ConnectionStatus.pending => ('Bekliyor', AppColors.pending), ConnectionStatus.rejected => ('Reddedildi', AppColors.cancelled), }; return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(6), ), child: Text(label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: color)), ); } }