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
+79 -83
View File
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../l10n/app_strings.dart';
import '../providers/locale_provider.dart';
import '../theme/app_theme.dart';
import '../widgets/tooth_logo.dart';
import '../providers/auth_provider.dart';
@@ -213,63 +215,60 @@ class _ClinicShell extends ConsumerStatefulWidget {
class _ClinicShellState extends ConsumerState<_ClinicShell> {
String _selectedRoute = routeClinicDashboard;
// Top-level singles before groups
static final _topSingles = [
_NavItem(route: routeClinicDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true),
_NavItem(route: routeClinicJobs, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeClinicPatients, icon: const Icon(Icons.people_outline_rounded), selectedIcon: const Icon(Icons.people_rounded), label: 'Hastalar', visible: (m) => m?.showPatients ?? true),
_NavItem(route: routeClinicFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeClinicAi, icon: const Icon(Icons.auto_awesome_outlined), selectedIcon: const Icon(Icons.auto_awesome_rounded), label: 'AI Sohbet', visible: (_) => true),
];
List<_NavItem> _clinicTopSingles(AppStrings s) => [
_NavItem(route: routeClinicDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: s.homeTitle, visible: (_) => true),
_NavItem(route: routeClinicJobs, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: s.jobsTitle, visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeClinicPatients, icon: const Icon(Icons.people_outline_rounded), selectedIcon: const Icon(Icons.people_rounded), label: s.patientsTitle, visible: (m) => m?.showPatients ?? true),
_NavItem(route: routeClinicFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: s.finance, visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeClinicAi, icon: const Icon(Icons.auto_awesome_outlined), selectedIcon: const Icon(Icons.auto_awesome_rounded), label: s.aiAssistant, visible: (_) => true),
];
// Dropdown groups
static final _groups = [
_NavGroup(
title: 'Yönetim',
icon: Icons.tune_rounded,
selectedIcon: Icons.tune_rounded,
items: [
_NavItem(route: routeClinicConnections, icon: const Icon(Icons.link_rounded), selectedIcon: const Icon(Icons.link_rounded), label: 'Bağlantılar', visible: (_) => true),
_NavItem(route: routeClinicReports, icon: const Icon(Icons.bar_chart_outlined), selectedIcon: const Icon(Icons.bar_chart_rounded), label: 'Raporlar', visible: (_) => true),
],
),
];
List<_NavGroup> _clinicGroups(AppStrings s) => [
_NavGroup(
title: s.management,
icon: Icons.tune_rounded,
selectedIcon: Icons.tune_rounded,
items: [
_NavItem(route: routeClinicConnections, icon: const Icon(Icons.link_rounded), selectedIcon: const Icon(Icons.link_rounded), label: s.connections, visible: (_) => true),
_NavItem(route: routeClinicReports, icon: const Icon(Icons.bar_chart_outlined), selectedIcon: const Icon(Icons.bar_chart_rounded), label: s.reports, visible: (_) => true),
],
),
];
// Singles after groups
static final _bottomSingles = [
_NavItem(route: routeClinicSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true),
];
List<_NavItem> _clinicBottomSingles(AppStrings s) => [
_NavItem(route: routeClinicSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: s.settings, visible: (_) => true),
];
// Mobile bottom nav: core items; others accessed from settings
static final _mobileItems = [
_NavItem(route: routeClinicDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true),
_NavItem(route: routeClinicJobs, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeClinicPatients, icon: const Icon(Icons.people_outline_rounded), selectedIcon: const Icon(Icons.people_rounded), label: 'Hastalar', visible: (m) => m?.showPatients ?? true),
_NavItem(route: routeClinicFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeClinicSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true),
];
List<_NavItem> _clinicMobileItems(AppStrings s) => [
_NavItem(route: routeClinicDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: s.homeTitle, visible: (_) => true),
_NavItem(route: routeClinicJobs, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: s.jobsTitle, visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeClinicPatients, icon: const Icon(Icons.people_outline_rounded), selectedIcon: const Icon(Icons.people_rounded), label: s.patientsTitle, visible: (m) => m?.showPatients ?? true),
_NavItem(route: routeClinicFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: s.finance, visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeClinicSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: s.settings, visible: (_) => true),
];
List<_SidebarEntry> _allEntries() {
List<_SidebarEntry> _allEntries(AppStrings s) {
final membership = ref.read(authProvider).activeTenant;
final entries = <_SidebarEntry>[];
for (final s in _topSingles) {
if (s.visible(membership)) entries.add(_SidebarSingleEntry(s));
for (final item in _clinicTopSingles(s)) {
if (item.visible(membership)) entries.add(_SidebarSingleEntry(item));
}
for (final g in _groups) {
if (g.hasVisible(membership)) entries.add(_SidebarGroupEntry(g));
for (final group in _clinicGroups(s)) {
if (group.hasVisible(membership)) entries.add(_SidebarGroupEntry(group));
}
for (final s in _bottomSingles) {
if (s.visible(membership)) entries.add(_SidebarSingleEntry(s));
for (final item in _clinicBottomSingles(s)) {
if (item.visible(membership)) entries.add(_SidebarSingleEntry(item));
}
return entries;
}
@override
Widget build(BuildContext context) {
final s = ref.watch(stringsProvider);
final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint;
if (isDesktop) {
final entries = _allEntries();
final entries = _allEntries(s);
return Scaffold(
backgroundColor: AppColors.background,
body: Row(
@@ -290,7 +289,7 @@ class _ClinicShellState extends ConsumerState<_ClinicShell> {
// Mobile: only core items in bottom nav
final membership = ref.read(authProvider).activeTenant;
final items = _mobileItems.where((it) => it.visible(membership)).toList();
final items = _clinicMobileItems(s).where((it) => it.visible(membership)).toList();
final flatIndex = items.indexWhere((it) => it.route == _selectedRoute);
final clampedIndex = flatIndex >= 0 ? flatIndex : 0;
@@ -339,64 +338,61 @@ class _LabShell extends ConsumerStatefulWidget {
class _LabShellState extends ConsumerState<_LabShell> {
String _selectedRoute = routeLabDashboard;
// Top-level singles before groups
static final _topSingles = [
_NavItem(route: routeLabDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true),
_NavItem(route: routeLabJobsAll, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeLabProducts, icon: const Icon(Icons.inventory_2_outlined), selectedIcon: const Icon(Icons.inventory_2_rounded), label: 'Ürünler', visible: (m) => m?.showProducts ?? true),
_NavItem(route: routeLabFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeLabAi, icon: const Icon(Icons.auto_awesome_outlined), selectedIcon: const Icon(Icons.auto_awesome_rounded), label: 'AI Sohbet', visible: (_) => true),
];
List<_NavItem> _labTopSingles(AppStrings s) => [
_NavItem(route: routeLabDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: s.homeTitle, visible: (_) => true),
_NavItem(route: routeLabJobsAll, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: s.jobsTitle, visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeLabProducts, icon: const Icon(Icons.inventory_2_outlined), selectedIcon: const Icon(Icons.inventory_2_rounded), label: s.productsTitle, visible: (m) => m?.showProducts ?? true),
_NavItem(route: routeLabFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: s.finance, visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeLabAi, icon: const Icon(Icons.auto_awesome_outlined), selectedIcon: const Icon(Icons.auto_awesome_rounded), label: s.aiAssistant, visible: (_) => true),
];
// Dropdown groups
static final _groups = [
_NavGroup(
title: 'Yönetim',
icon: Icons.tune_rounded,
selectedIcon: Icons.tune_rounded,
items: [
_NavItem(route: routeLabConnections, icon: const Icon(Icons.link_rounded), selectedIcon: const Icon(Icons.link_rounded), label: 'Bağlantılar', visible: (_) => true),
_NavItem(route: routeLabDiscounts, icon: const Icon(Icons.local_offer_outlined), selectedIcon: const Icon(Icons.local_offer_rounded), label: 'İndirimler', visible: (_) => true),
_NavItem(route: routeLabReports, icon: const Icon(Icons.bar_chart_outlined), selectedIcon: const Icon(Icons.bar_chart_rounded), label: 'Raporlar', visible: (_) => true),
],
),
];
List<_NavGroup> _labGroups(AppStrings s) => [
_NavGroup(
title: s.management,
icon: Icons.tune_rounded,
selectedIcon: Icons.tune_rounded,
items: [
_NavItem(route: routeLabConnections, icon: const Icon(Icons.link_rounded), selectedIcon: const Icon(Icons.link_rounded), label: s.connections, visible: (_) => true),
_NavItem(route: routeLabDiscounts, icon: const Icon(Icons.local_offer_outlined), selectedIcon: const Icon(Icons.local_offer_rounded), label: s.discounts, visible: (_) => true),
_NavItem(route: routeLabReports, icon: const Icon(Icons.bar_chart_outlined), selectedIcon: const Icon(Icons.bar_chart_rounded), label: s.reports, visible: (_) => true),
],
),
];
// Singles after groups
static final _bottomSingles = [
_NavItem(route: routeLabSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true),
];
List<_NavItem> _labBottomSingles(AppStrings s) => [
_NavItem(route: routeLabSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: s.settings, visible: (_) => true),
];
// Mobile bottom nav: core items; others accessed from settings
static final _mobileItems = [
_NavItem(route: routeLabDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true),
_NavItem(route: routeLabJobsAll, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeLabProducts, icon: const Icon(Icons.inventory_2_outlined), selectedIcon: const Icon(Icons.inventory_2_rounded), label: 'Ürünler', visible: (m) => m?.showProducts ?? true),
_NavItem(route: routeLabFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeLabSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true),
];
List<_NavItem> _labMobileItems(AppStrings s) => [
_NavItem(route: routeLabDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: s.homeTitle, visible: (_) => true),
_NavItem(route: routeLabJobsAll, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: s.jobsTitle, visible: (m) => m?.showJobs ?? true),
_NavItem(route: routeLabProducts, icon: const Icon(Icons.inventory_2_outlined), selectedIcon: const Icon(Icons.inventory_2_rounded), label: s.productsTitle, visible: (m) => m?.showProducts ?? true),
_NavItem(route: routeLabFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: s.finance, visible: (m) => m?.showFinance ?? true),
_NavItem(route: routeLabSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: s.settings, visible: (_) => true),
];
List<_SidebarEntry> _allEntries() {
List<_SidebarEntry> _allEntries(AppStrings s) {
final membership = ref.read(authProvider).activeTenant;
final entries = <_SidebarEntry>[];
for (final s in _topSingles) {
if (s.visible(membership)) entries.add(_SidebarSingleEntry(s));
for (final item in _labTopSingles(s)) {
if (item.visible(membership)) entries.add(_SidebarSingleEntry(item));
}
for (final g in _groups) {
if (g.hasVisible(membership)) entries.add(_SidebarGroupEntry(g));
for (final group in _labGroups(s)) {
if (group.hasVisible(membership)) entries.add(_SidebarGroupEntry(group));
}
for (final s in _bottomSingles) {
if (s.visible(membership)) entries.add(_SidebarSingleEntry(s));
for (final item in _labBottomSingles(s)) {
if (item.visible(membership)) entries.add(_SidebarSingleEntry(item));
}
return entries;
}
@override
Widget build(BuildContext context) {
final s = ref.watch(stringsProvider);
final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint;
if (isDesktop) {
final entries = _allEntries();
final entries = _allEntries(s);
return Scaffold(
backgroundColor: AppColors.background,
body: Row(
@@ -417,7 +413,7 @@ class _LabShellState extends ConsumerState<_LabShell> {
// Mobile: only core items in bottom nav
final membership = ref.read(authProvider).activeTenant;
final items = _mobileItems.where((it) => it.visible(membership)).toList();
final items = _labMobileItems(s).where((it) => it.visible(membership)).toList();
final flatIndex = items.indexWhere((it) => it.route == _selectedRoute);
final clampedIndex = flatIndex >= 0 ? flatIndex : 0;