This commit is contained in:
2026-10-01 16:03:32 +05:30
commit 3db47c4648
102 changed files with 5067 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/models.dart';
import '../services/auth_controller.dart';
import '../theme/app_theme.dart';
import '../ui/feedback.dart';
import 'dealer_stock_screen.dart';
import 'stock_form_screen.dart';
class StockScreen extends StatefulWidget {
const StockScreen({super.key});
@override
State<StockScreen> createState() => _StockScreenState();
}
class _StockScreenState extends State<StockScreen> {
List<Dealer> _dealers = [];
List<StockItem> _stocks = [];
bool _loading = true;
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
final api = context.read<AuthController>().api;
try {
final results = await Future.wait([
api.dealers(),
api.stocks(),
]);
if (!mounted) return;
setState(() {
_dealers = results[0] as List<Dealer>;
_stocks = results[1] as List<StockItem>;
_loading = false;
});
} catch (error) {
if (!mounted) return;
setState(() => _loading = false);
showError(context, error);
}
}
int _count(String dealerId, String badge) {
return _stocks.where((item) => item.currentDealerId == dealerId && item.badge == badge).length;
}
@override
Widget build(BuildContext context) {
final role = context.watch<AuthController>().user?.role;
final canAdd = role == 'dealer' || role == 'salesRep' || role == 'superAdmin';
if (_loading && _dealers.isEmpty && _stocks.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
return Scaffold(
floatingActionButton: canAdd
? FloatingActionButton.extended(
onPressed: () async {
final created = await Navigator.of(context).push<bool>(
MaterialPageRoute(builder: (_) => StockFormScreen(dealers: _dealers)),
);
if (created == true) _load();
},
icon: const Icon(Icons.add),
label: const Text('Add stock'),
)
: null,
body: RefreshIndicator(
onRefresh: _load,
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 96),
children: [
if (_dealers.isEmpty)
const Padding(
padding: EdgeInsets.symmetric(vertical: 32),
child: Center(child: Text('No dealers to show.')),
),
for (final dealer in _dealers)
Card(
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () async {
await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (_) => DealerStockScreen(dealer: dealer, dealers: _dealers),
),
);
if (mounted) _load();
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(dealer.name, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 2),
Text(
dealer.district == null || dealer.district!.isEmpty
? dealer.code
: '${dealer.code} · ${dealer.district}',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _AgeCount(
label: 'Under 30',
count: _count(dealer.id, 'green'),
badge: 'green',
),
),
Expanded(
child: _AgeCount(
label: '30–45',
count: _count(dealer.id, 'yellow'),
badge: 'yellow',
),
),
Expanded(
child: _AgeCount(
label: 'Over 45',
count: _count(dealer.id, 'red'),
badge: 'red',
),
),
],
),
],
),
),
),
),
],
),
),
);
}
}
class _AgeCount extends StatelessWidget {
const _AgeCount({required this.label, required this.count, required this.badge});
final String label;
final int count;
final String badge;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'$count',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: badgeColor(badge)),
),
const SizedBox(height: 2),
Text(label, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center),
],
);
}
}