sc-android-app/lib/screens/add_sales_product_screen.dart
2024-09-11 18:32:52 +05:30

419 lines
15 KiB
Dart

import 'package:cheminova/models/SalesTaskResponse.dart';
import 'package:cheminova/provider/add_sales_product_provider.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class AddSalesProductScreen extends StatefulWidget {
final String distributorType;
final String tradeName;
final String pdRdId;
final String? inventoryId;
const AddSalesProductScreen({
super.key,
required this.distributorType,
required this.tradeName,
required this.pdRdId,
this.inventoryId,
});
@override
State<AddSalesProductScreen> createState() => _AddSalesProductScreenState();
}
class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
final searchController = TextEditingController();
late AddSalesProvider salesTaskProvider;
final dateController = TextEditingController();
final formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
salesTaskProvider = Provider.of<AddSalesProvider>(context, listen: false);
dateController.text = DateFormat('dd/MM/yyyy').format(DateTime.now());
WidgetsBinding.instance.addPostFrameCallback((_) {
salesTaskProvider.getTask();
});
}
@override
void dispose() {
searchController.dispose();
dateController.dispose();
if (mounted) {
salesTaskProvider.resetProducts();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: true,
child: CommonBackground(
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: CommonAppBar(
actions: [
IconButton(
onPressed: () => Navigator.pop(context),
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
)
],
title: Text(
'${widget.distributorType}\n${widget.tradeName}',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek',
),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
drawer: const CommonDrawer(),
bottomNavigationBar: Consumer<AddSalesProvider>(
builder: (context, value, child) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: value.tasksList.isEmpty
? Alignment.center
: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.extended(
onPressed: () => _showProductSelectionBottomSheet(context),
backgroundColor: Colors.white,
icon: const Icon(Icons.add, color: Colors.black),
label: const Text('Add Products', style: TextStyle(color: Colors.black)),
),
if (value.selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0),
CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () => _submitProducts(value),
),
],
],
),
),
),
],
),
),
body: Consumer<AddSalesProvider>(
builder: (context, value, child) {
return Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: dateController,
readOnly: true,
decoration: const InputDecoration(
labelText: 'Date',
fillColor: Colors.white,
filled: true,
border: InputBorder.none,
suffixIcon: Icon(Icons.calendar_today),
),
),
),
if (value.selectedProducts.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: value.selectedProducts.length,
itemBuilder: (context, index) {
return ProductBlock(
onUpdate: (updatedProduct) {
setState(() {
value.selectedProducts[index] = updatedProduct;
});
},
onRemove: () {
setState(() {
value.selectedProducts.removeAt(index);
});
},
product: value.selectedProducts[index],
);
},
),
),
],
),
if (value.isLoading)
Container(
color: Colors.black12,
child: const Center(child: CircularProgressIndicator()),
),
],
);
},
),
),
),
);
}
void _showProductSelectionBottomSheet(BuildContext context) {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Consumer<AddSalesProvider>(
builder: (context, value, child) => StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(18.0),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText: 'Search by name or SKU',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onChanged: (val) {
value.filterProducts(val);
setState(() {});
},
),
),
Expanded(
child: ListView.builder(
itemCount: searchController.text.isEmpty
? value.tasksList.length
: value.searchList.length,
itemBuilder: (context, index) {
final data = searchController.text.isEmpty
? value.tasksList[index]
: value.searchList[index];
bool isAlreadySelected = value.selectedProducts
.any((selectedProduct) => selectedProduct.SKU == data.SKU);
return Card(
child: ListTile(
title: Text(
data.ProductName ?? '',
style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black),
),
subtitle: Text(
data.SKU ?? '',
style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black),
),
onTap: isAlreadySelected
? null
: () {
setState(() => value.selectedProducts.add(data));
Navigator.pop(context);
},
),
);
},
),
),
],
);
},
),
);
},
).whenComplete(() => setState(() {}));
}
void _submitProducts(AddSalesProvider value) {
if (formKey.currentState!.validate()) {
if (value.selectedProducts.isNotEmpty &&
value.selectedProducts.every((product) =>
product.SKU!.isNotEmpty &&
product.ProductName!.isNotEmpty &&
product.SalesAmount != null &&
product.QuantitySold != null)) {
value.submitProducts(
distributorType: widget.distributorType,
pdRdId: widget.pdRdId,
inventoryId: widget.inventoryId,
date: dateController.text.trim(),
tradeName: widget.tradeName,
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please fill out all product details, including sale and inventory.'),
),
);
}
}
}
}
// The ProductBlock class remains unchanged
class ProductBlock extends StatefulWidget {
final SalesProduct product;
final ValueChanged<SalesProduct> onUpdate;
final VoidCallback onRemove;
const ProductBlock({
super.key,
required this.product,
required this.onUpdate,
required this.onRemove,
});
@override
State<ProductBlock> createState() => _ProductBlockState();
}
class _ProductBlockState extends State<ProductBlock> {
final saleAmountController = TextEditingController();
final quantitySoldController = TextEditingController();
final commentController = TextEditingController();
String? errorMessage;
@override
void initState() {
super.initState();
saleAmountController.text = (widget.product.SalesAmount ?? '').toString();
commentController.text = (widget.product.comments ?? '').toString();
quantitySoldController.text = (widget.product.QuantitySold ?? '').toString();
}
void validateInput() {
setState(() {
String? quantitySoldError;
String? salesAmountError;
if (saleAmountController.text.isEmpty) {
quantitySoldError = 'Quantity sold cannot be empty.';
}
if (quantitySoldController.text.isEmpty) {
salesAmountError = 'Sales amount cannot be empty.';
}
errorMessage = null;
if (quantitySoldError == null && salesAmountError == null) {
int quantitySold = int.parse(quantitySoldController.text);
int salesAmount = int.parse(saleAmountController.text);
String comments = commentController.text;
widget.onUpdate(SalesProduct(
SKU: widget.product.SKU,
ProductName: widget.product.ProductName,
QuantitySold: quantitySold,
SalesAmount: salesAmount,
comments: comments,
));
} else {
errorMessage = quantitySoldError ?? salesAmountError;
}
});
}
@override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
margin: const EdgeInsets.all(8),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Product: ${widget.product.ProductName}',
style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.SKU}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: saleAmountController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Sales amount',
errorText: saleAmountController.text.isEmpty
? 'Sales amount cannot be empty.'
: null,
),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput(),
),
TextField(
controller: quantitySoldController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Quantity sold',
errorText: quantitySoldController.text.isEmpty
? 'Quantity sold cannot be empty.'
: null,
),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput(),
),
TextField(
controller: commentController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
decoration: const InputDecoration(
labelText: 'Comments',
),
enabled: true,
onChanged: (_) => validateInput(),
),
],
),
],
),
),
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: const Icon(
Icons.delete_outlined,
color: Colors.red,
),
onPressed: widget.onRemove,
),
),
],
),
);
}
@override
void dispose() {
saleAmountController.dispose();
quantitySoldController.dispose();
commentController.dispose();
super.dispose();
}
}