Fixed bugs

This commit is contained in:
2026-08-20 22:02:38 +05:30
parent 5f620022f3
commit fc0faafa18
7 changed files with 198 additions and 22 deletions

View File

@@ -8,6 +8,8 @@ class SmartSearchDropdown<T> extends StatefulWidget {
final String hintText;
final Widget Function(BuildContext, T)? itemBuilder;
final bool Function(T, String)? filterFn;
final Color? fillColor;
final String? labelText;
const SmartSearchDropdown({
super.key,
@@ -18,6 +20,8 @@ class SmartSearchDropdown<T> extends StatefulWidget {
this.hintText = 'Type to search...',
this.itemBuilder,
this.filterFn,
this.fillColor,
this.labelText,
});
@override
@@ -198,10 +202,12 @@ class _SmartSearchDropdownState<T> extends State<SmartSearchDropdown<T>> {
controller: _controller,
focusNode: _focusNode,
decoration: InputDecoration(
labelText: widget.labelText,
labelStyle: TextStyle(color: Theme.of(context).brightness == Brightness.dark ? Colors.white70 : Colors.grey[600]),
hintText: widget.hintText,
suffixIcon: const Icon(Icons.arrow_drop_down, color: Colors.grey),
filled: Theme.of(context).inputDecorationTheme.filled ?? true,
fillColor: Theme.of(context).inputDecorationTheme.fillColor ?? Colors.grey[100],
fillColor: widget.fillColor ?? (Theme.of(context).brightness == Brightness.dark ? Colors.black26 : Colors.grey[100]),
border: Theme.of(context).inputDecorationTheme.border ?? OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
enabledBorder: Theme.of(context).inputDecorationTheme.enabledBorder ?? OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: Theme.of(context).inputDecorationTheme.focusedBorder ?? OutlineInputBorder(

View File

@@ -9,6 +9,7 @@ import '../../../sales/presentation/customers_list_screen.dart';
import '../../../sales/presentation/invoices_list_screen.dart';
import '../../providers/business_provider.dart';
import '../widgets/business_profile_form_sheet.dart';
import '../../../inventory/providers/products_provider.dart';
class BusinessHubScreen extends ConsumerWidget {
const BusinessHubScreen({super.key});
@@ -126,17 +127,97 @@ class BusinessHubScreen extends ConsumerWidget {
if (showInventory) ...[
const SizedBox(height: 32),
Text('Low Stock Alerts', style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
// Placeholder for low stock items
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
),
child: const Center(
child: Text('All products are adequately stocked.', style: TextStyle(color: Colors.grey)),
),
Consumer(
builder: (context, ref, child) {
final productsState = ref.watch(productsProvider);
return productsState.when(
data: (products) {
final lowStockProducts = products.where((p) {
if (!(p.trackInventory ?? false)) return false;
if (p.currentStock == null) return false;
final minStock = p.minStock ?? 0;
return p.currentStock! <= minStock;
}).toList();
if (lowStockProducts.isEmpty) {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
),
child: const Center(
child: Text('All products are adequately stocked.', style: TextStyle(color: Colors.grey)),
),
);
}
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: lowStockProducts.length > 5 ? 5 : lowStockProducts.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemBuilder: (context, index) {
final product = lowStockProducts[index];
final isOutOfStock = product.currentStock! <= 0;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isOutOfStock ? Colors.red.withOpacity(0.05) : Colors.orange.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: isOutOfStock ? Colors.red.withOpacity(0.2) : Colors.orange.withOpacity(0.2)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isOutOfStock ? Colors.red.withOpacity(0.1) : Colors.orange.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.alertTriangle,
color: isOutOfStock ? Colors.red : Colors.orange,
size: 20,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(product.name, style: const TextStyle(fontWeight: FontWeight.bold)),
if (product.sku != null && product.sku!.isNotEmpty)
Text('SKU: ${product.sku}', style: TextStyle(fontSize: 12, color: Colors.grey.shade600)),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${product.currentStock} left',
style: TextStyle(
fontWeight: FontWeight.bold,
color: isOutOfStock ? Colors.red : Colors.orange.shade800,
),
),
Text(
'Min: ${product.minStock ?? 0}',
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
),
],
),
],
),
);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
);
},
),
],
],

View File

@@ -18,6 +18,7 @@ import '../../sales/domain/invoice.dart';
import '../../sales/presentation/customers_list_screen.dart';
import 'widgets/swipeable_account_card.dart';
import 'widgets/budget_status_card.dart';
import '../../inventory/presentation/daily_rates_screen.dart';
import 'widgets/upcoming_dues_widget.dart';
import 'widgets/statistics_tab.dart';
import '../../../core/widgets/shimmer_loading.dart';
@@ -176,6 +177,16 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
icon: const Icon(LucideIcons.trendingUp),
tooltip: 'Daily Commodity Rates',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const DailyRatesScreen()),
);
},
),
Consumer(
builder: (context, ref, child) {
final invitationsState = ref.watch(invitationProvider);

View File

@@ -135,7 +135,12 @@ class _DailyRatesScreenState extends ConsumerState<DailyRatesScreen> {
);
}
return ListView.builder(
return RefreshIndicator(
onRefresh: () async {
ref.invalidate(productCategoriesProvider);
await ref.read(productCategoriesProvider.future);
},
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: commodities.length,
itemBuilder: (context, index) {
@@ -314,7 +319,7 @@ class _DailyRatesScreenState extends ConsumerState<DailyRatesScreen> {
),
);
},
);
));
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Center(child: Text('Error: $err')),

View File

@@ -163,7 +163,7 @@ class _AddCustomerSheetState extends ConsumerState<AddCustomerSheet> {
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 24,
right: 24,
top: 24,
top: 48,
),
child: Form(
key: _formKey,
@@ -173,8 +173,14 @@ class _AddCustomerSheetState extends ConsumerState<AddCustomerSheet> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(LucideIcons.chevronLeft, size: 28),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () => Navigator.pop(context),
),
const SizedBox(width: 12),
Text(widget.customer == null ? "New Customer" : "Edit Customer", style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
],
),
@@ -305,8 +311,10 @@ class _AddCustomerSheetState extends ConsumerState<AddCustomerSheet> {
return statesState.when(
data: (states) {
return SmartSearchDropdown<int>(
labelText: 'State',
hintText: 'Select State',
value: _selectedStateId,
fillColor: Colors.grey[100],
items: states.map((s) => s.id).toList(),
itemAsString: (id) {
final s = states.firstWhere((st) => st.id == id);

View File

@@ -450,6 +450,34 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
const Text('Line Items', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
Row(
children: [
Consumer(
builder: (context, ref, child) {
final feature = ref.watch(businessFeatureProvider).value;
final isBarcode = feature?.barcodeSource == 'BARCODE';
return Row(
children: [
Text('Scan by:', style: TextStyle(fontSize: 12, color: Colors.grey.shade600)),
const SizedBox(width: 4),
Text('SKU', style: TextStyle(fontSize: 10, fontWeight: !isBarcode ? FontWeight.bold : FontWeight.normal, color: !isBarcode ? Colors.blue : Colors.grey)),
Transform.scale(
scale: 0.7,
child: Switch(
value: isBarcode,
activeColor: Colors.blue,
onChanged: (val) {
if (feature != null) {
ref.read(businessFeatureProvider.notifier).updateFeatures(
feature.copyWith(barcodeSource: val ? 'BARCODE' : 'SKU')
);
}
},
),
),
Text('EAN', style: TextStyle(fontSize: 10, fontWeight: isBarcode ? FontWeight.bold : FontWeight.normal, color: isBarcode ? Colors.blue : Colors.grey)),
],
);
},
),
IconButton(
icon: const Icon(LucideIcons.scanLine, color: Colors.blue),
onPressed: _scanAndAddBarcode,
@@ -595,10 +623,25 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
child: Consumer(
builder: (context, consumerRef, _) {
final walletsState = consumerRef.watch(walletProvider);
final wallets = walletsState.value ?? [];
final allWallets = walletsState.value ?? [];
final wallets = allWallets.where((w) {
if (_paymentMethod == 'Cash') {
return w.nature == 'CASH';
} else {
return w.nature == 'INCOME' || w.nature == 'SAVINGS';
}
}).toList();
if (wallets.isNotEmpty && (_selectedWalletId == null || !wallets.any((w) => w.id == _selectedWalletId))) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) setState(() => _selectedWalletId = wallets.first.id);
});
}
return DropdownButtonHideUnderline(
child: DropdownButton<int>(
value: _selectedWalletId ?? wallets.firstOrNull?.id,
value: _selectedWalletId,
isDense: true,
hint: const Text('Wallet'),
items: wallets
@@ -626,7 +669,14 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
items: ['Cash', 'UPI', 'Bank Transfer', 'Card']
.map((m) => DropdownMenuItem(value: m, child: Text(m)))
.toList(),
onChanged: (val) => setState(() => _paymentMethod = val!),
onChanged: (val) {
if (val != null) {
setState(() {
_paymentMethod = val;
_selectedWalletId = null;
});
}
},
),
),
),

View File

@@ -31,9 +31,17 @@ class _ReceivePaymentSheetState extends ConsumerState<ReceivePaymentSheet> {
@override
Widget build(BuildContext context) {
final walletsState = ref.watch(walletProvider);
final wallets = walletsState.value ?? [];
final allWallets = walletsState.value ?? [];
if (wallets.isNotEmpty && _selectedWalletId == null) {
final wallets = allWallets.where((w) {
if (_paymentMethod == 'Cash') {
return w.nature == 'CASH';
} else {
return w.nature == 'INCOME' || w.nature == 'SAVINGS';
}
}).toList();
if (wallets.isNotEmpty && (_selectedWalletId == null || !wallets.any((w) => w.id == _selectedWalletId))) {
_selectedWalletId = wallets.first.id;
}
@@ -102,7 +110,14 @@ class _ReceivePaymentSheetState extends ConsumerState<ReceivePaymentSheet> {
items: ['Cash', 'UPI', 'Bank Transfer', 'Card']
.map((m) => DropdownMenuItem(value: m, child: Text(m)))
.toList(),
onChanged: (val) => setState(() => _paymentMethod = val!),
onChanged: (val) {
if (val != null) {
setState(() {
_paymentMethod = val;
_selectedWalletId = null;
});
}
},
),
),
),