feat: improve patient flow and pricing workflow

This commit is contained in:
egecankomur
2026-06-12 00:04:53 +03:00
parent e12587398b
commit b42f68214e
26 changed files with 1283 additions and 243 deletions
@@ -23,7 +23,7 @@ class ClinicFinanceScreen extends ConsumerStatefulWidget {
class _ClinicFinanceScreenState extends ConsumerState<ClinicFinanceScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late Future<Map<String, double>> _summaryFuture;
late Future<_ClinicFinanceHeaderData> _headerFuture;
_FinanceSort _sort = _FinanceSort.newestFirst;
@override
@@ -45,8 +45,15 @@ class _ClinicFinanceScreenState extends ConsumerState<ClinicFinanceScreen>
void _loadSummary() {
final tenantId = ref.read(authProvider).activeTenant!.tenant.id;
setState(() {
_summaryFuture =
ClinicFinanceRepository.instance.summary(tenantId);
_headerFuture = Future.wait([
ClinicFinanceRepository.instance.summary(tenantId),
ClinicFinanceRepository.instance.byCounterparty(tenantId),
]).then(
(results) => _ClinicFinanceHeaderData(
summary: results[0] as Map<String, double>,
counterparties: results[1] as List<CounterpartyFinanceSummary>,
),
);
});
}
@@ -90,41 +97,58 @@ class _ClinicFinanceScreenState extends ConsumerState<ClinicFinanceScreen>
),
body: Column(
children: [
FutureBuilder<Map<String, double>>(
future: _summaryFuture,
FutureBuilder<_ClinicFinanceHeaderData>(
future: _headerFuture,
builder: (ctx, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const LinearProgressIndicator(
color: AppColors.accent);
}
final summary = snap.data ?? {'pending': 0.0, 'paid': 0.0};
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: _SummaryCard(
label: s.pendingReceivable,
amount: summary['pending'] ?? 0.0,
final data = snap.data ??
const _ClinicFinanceHeaderData(
summary: {'pending': 0.0, 'paid': 0.0},
counterparties: [],
);
return Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: _SummaryCard(
label: s.pendingReceivable,
amount: data.summary['pending'] ?? 0.0,
currencyCode: currencyCode,
color: AppColors.pending,
bgColor: AppColors.pendingBg,
icon: Icons.hourglass_empty_rounded,
),
),
const SizedBox(width: 12),
Expanded(
child: _SummaryCard(
label: s.collected,
amount: data.summary['paid'] ?? 0.0,
currencyCode: currencyCode,
color: AppColors.success,
bgColor: AppColors.successBg,
icon: Icons.check_circle_outline,
),
),
],
),
),
if (data.counterparties.isNotEmpty)
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
child: _CounterpartySummaryList(
title: 'Laboratuvar Bazlı Borç',
items: data.counterparties,
currencyCode: currencyCode,
color: AppColors.pending,
bgColor: AppColors.pendingBg,
icon: Icons.hourglass_empty_rounded,
),
),
const SizedBox(width: 12),
Expanded(
child: _SummaryCard(
label: s.collected,
amount: summary['paid'] ?? 0.0,
currencyCode: currencyCode,
color: AppColors.success,
bgColor: AppColors.successBg,
icon: Icons.check_circle_outline,
),
),
],
),
],
);
},
),
@@ -158,6 +182,16 @@ class _ClinicFinanceScreenState extends ConsumerState<ClinicFinanceScreen>
}
}
class _ClinicFinanceHeaderData {
const _ClinicFinanceHeaderData({
required this.summary,
required this.counterparties,
});
final Map<String, double> summary;
final List<CounterpartyFinanceSummary> counterparties;
}
class _SummaryCard extends StatelessWidget {
const _SummaryCard({
required this.label,
@@ -532,3 +566,66 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
}
}
}
class _CounterpartySummaryList extends StatelessWidget {
const _CounterpartySummaryList({
required this.title,
required this.items,
required this.currencyCode,
});
final String title;
final List<CounterpartyFinanceSummary> items;
final String currencyCode;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 10),
for (final item in items.take(5)) ...[
Row(
children: [
Expanded(
child: Text(
item.counterpartyName,
style: const TextStyle(
fontSize: 13,
color: AppColors.textPrimary,
fontWeight: FontWeight.w600,
),
),
),
Text(
CurrencyFormatter.format(item.pendingAmount, currencyCode),
style: const TextStyle(
fontSize: 13,
color: AppColors.pending,
fontWeight: FontWeight.w700,
),
),
],
),
const SizedBox(height: 8),
],
],
),
);
}
}