Files
Kifi/kifi-app/lib/core/widgets/desktop_sidebar.dart

459 lines
17 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../features/inventory/providers/commodity_rates_provider.dart';
import '../../features/business/providers/business_provider.dart';
import '../../features/business/providers/business_mode_provider.dart';
import '../../features/transactions/presentation/add_transaction_screen.dart';
import '../../features/sales/presentation/invoice_builder_screen.dart';
import '../../features/vendor/presentation/purchase_order_builder_screen.dart';
import '../../features/inventory/presentation/add_product_screen.dart';
import '../../features/auth/presentation/profile_screen.dart';
class DesktopSidebar extends ConsumerWidget {
final int selectedIndex;
final ValueChanged<int> onDestinationSelected;
const DesktopSidebar({
super.key,
required this.selectedIndex,
required this.onDestinationSelected,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
final isBusinessMode = ref.watch(businessModeProvider);
final businessProfile = ref.watch(businessProfileProvider).asData?.value;
final nature = (businessProfile?.natureOfBusiness ?? businessProfile?.industry ?? 'JEWELLERY').toUpperCase();
final isJewellery = nature == 'JEWELLERY';
final ratesAsync = ref.watch(commodityRatesProvider);
return Container(
width: 260,
decoration: BoxDecoration(
color: isDark ? const Color(0xFF131720) : Colors.white,
border: Border(
right: BorderSide(
color: isDark ? Colors.white10 : Colors.black12,
width: 1,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. Brand Logo Header
Padding(
padding: const EdgeInsets.fromLTRB(20, 24, 20, 16),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/icon.png',
width: 40,
height: 40,
fit: BoxFit.cover,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'KIFI',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
letterSpacing: 1.2,
color: isDark ? Colors.white : const Color(0xFF1E293B),
),
),
Text(
businessProfile?.businessName ?? 'Financial Ledger',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white54 : Colors.black45,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
// 2. Quick Action Button
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: PopupMenuButton<String>(
onSelected: (action) {
switch (action) {
case 'transaction':
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const AddTransactionScreen()),
);
break;
case 'invoice':
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const InvoiceBuilderScreen()),
);
break;
case 'po':
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const PurchaseOrderBuilderScreen()),
);
break;
case 'product':
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const AddProductScreen()),
);
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'transaction',
child: Row(
children: [
Icon(LucideIcons.arrowDownUp, size: 18, color: Colors.blueAccent),
SizedBox(width: 12),
Text('New Transaction'),
],
),
),
if (isBusinessMode) ...[
const PopupMenuItem(
value: 'invoice',
child: Row(
children: [
Icon(LucideIcons.fileText, size: 18, color: Colors.green),
SizedBox(width: 12),
Text('New Sales Invoice'),
],
),
),
const PopupMenuItem(
value: 'po',
child: Row(
children: [
Icon(LucideIcons.shoppingBag, size: 18, color: Colors.purpleAccent),
SizedBox(width: 12),
Text('New Purchase Order'),
],
),
),
const PopupMenuItem(
value: 'product',
child: Row(
children: [
Icon(LucideIcons.packagePlus, size: 18, color: Color(0xFFD4AF37)),
SizedBox(width: 12),
Text('Add Inventory Product'),
],
),
),
],
],
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isDark
? [const Color(0xFF2563EB), const Color(0xFF1D4ED8)]
: [const Color(0xFF3B82F6), const Color(0xFF2563EB)],
),
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: const Color(0xFF2563EB).withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 3),
),
],
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.plus, color: Colors.white, size: 18),
SizedBox(width: 8),
Text(
'Create New',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 13,
),
),
Spacer(),
Icon(LucideIcons.chevronDown, color: Colors.white70, size: 16),
],
),
),
),
),
const SizedBox(height: 8),
// 3. Navigation List
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 12),
children: [
_buildNavItem(
context,
index: 0,
icon: LucideIcons.layoutDashboard,
label: 'Dashboard',
isSelected: selectedIndex == 0,
),
_buildNavItem(
context,
index: 1,
icon: LucideIcons.arrowDownUp,
label: 'Transactions',
isSelected: selectedIndex == 1,
),
_buildNavItem(
context,
index: 2,
icon: LucideIcons.wallet,
label: 'Wallets & Accounts',
isSelected: selectedIndex == 2,
),
_buildNavItem(
context,
index: 3,
icon: LucideIcons.target,
label: 'Budgets & Goals',
isSelected: selectedIndex == 3,
),
if (isBusinessMode) ...[
const Padding(
padding: EdgeInsets.fromLTRB(12, 20, 12, 8),
child: Text(
'BUSINESS & ERP',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 1.1,
color: Colors.grey,
),
),
),
_buildNavItem(
context,
index: 4,
icon: LucideIcons.briefcase,
label: 'Business Hub',
isSelected: selectedIndex == 4,
),
],
],
),
),
// 4. Live Metal Rate Widget in Sidebar (Jewellery only)
if (isJewellery)
ratesAsync.when(
data: (rates) {
if (rates.isEmpty) return const SizedBox.shrink();
final goldRate = rates.firstWhere(
(r) => r.commodityCode == 'GOLD',
orElse: () => rates.first,
);
return Container(
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1E2330) : const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: const Color(0xFFD4AF37).withValues(alpha: 0.2),
),
),
child: Row(
children: [
const Icon(LucideIcons.coins, color: Color(0xFFD4AF37), size: 18),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Gold (24K/10g)',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white60 : Colors.black54,
),
),
Text(
'${(goldRate.rate * 10).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Color(0xFFD4AF37),
),
),
],
),
),
const Icon(LucideIcons.trendingUp, color: Colors.green, size: 16),
],
),
);
},
loading: () => const SizedBox.shrink(),
error: (_, __) => const SizedBox.shrink(),
),
// 5. Sidebar Footer (Profile & Settings)
Container(
padding: const EdgeInsets.fromLTRB(14, 12, 14, 16),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: isDark ? Colors.white10 : Colors.black12,
),
),
),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const ProfileScreen()),
);
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(4),
child: Row(
children: [
CircleAvatar(
radius: 16,
backgroundColor: isDark ? const Color(0xFF2563EB) : const Color(0xFF3B82F6),
child: const Icon(LucideIcons.user, size: 16, color: Colors.white),
),
const SizedBox(width: 10),
Expanded(
child: Text(
'Profile & Settings',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
),
IconButton(
tooltip: 'Settings',
icon: const Icon(LucideIcons.settings, size: 18),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const ProfileScreen()),
);
},
),
],
),
),
],
),
);
}
Widget _buildNavItem(
BuildContext context, {
required int index,
required IconData icon,
required String label,
required bool isSelected,
}) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: InkWell(
onTap: () => onDestinationSelected(index),
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: isSelected
? (isDark
? const Color(0xFF2563EB).withValues(alpha: 0.18)
: const Color(0xFF2563EB).withValues(alpha: 0.1))
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
border: isSelected
? Border.all(
color: const Color(0xFF2563EB).withValues(alpha: 0.3),
)
: null,
),
child: Row(
children: [
Icon(
icon,
size: 19,
color: isSelected
? const Color(0xFF3B82F6)
: (isDark ? Colors.white60 : Colors.black54),
),
const SizedBox(width: 12),
Text(
label,
style: TextStyle(
fontSize: 13,
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
color: isSelected
? (isDark ? Colors.white : const Color(0xFF1D4ED8))
: (isDark ? Colors.white70 : Colors.black87),
),
),
],
),
),
),
);
}
}