API of products and PD/RD & Validations

This commit is contained in:
Vaibhav 2024-08-16 17:54:32 +05:30
parent 600bcdd3d7
commit d734bbb6ee
16 changed files with 720 additions and 559 deletions

View File

@ -8,3 +8,6 @@ final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState> get scaffoldMessengerKey => GlobalKey<ScaffoldMessengerState> get scaffoldMessengerKey =>
_scaffoldMessengerKey; _scaffoldMessengerKey;
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

View File

@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/provider/home_provider.dart'; import 'package:cheminova/provider/home_provider.dart';
import 'package:cheminova/provider/products_provider.dart'; import 'package:cheminova/provider/products_provider.dart';
import 'package:cheminova/screens/splash_screen.dart'; import 'package:cheminova/screens/splash_screen.dart';
@ -106,7 +107,7 @@ class _MyAppState extends State<MyApp> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
// scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey, navigatorKey: navigatorKey,
title: 'cheminova', title: 'cheminova',
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),

View File

@ -0,0 +1,18 @@
class GetPdRdResponse {
String? sId;
String? name;
GetPdRdResponse({this.sId, this.name});
GetPdRdResponse.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['name'] = name;
return data;
}
}

View File

@ -1,181 +1,168 @@
class ProductResponse { class ProductResponse {
final bool success; bool? success;
final int totalData; int? totalData;
final int totalPages; int? totalPages;
final List<Product> product; List<Products>? products;
ProductResponse({ ProductResponse(
required this.success, {this.success, this.totalData, this.totalPages, this.products});
required this.totalData,
required this.totalPages, ProductResponse.fromJson(Map<String, dynamic> json) {
required this.product, success = json['success'];
totalData = json['total_data'];
totalPages = json['total_pages'];
if (json['products'] != null) {
products = <Products>[];
json['products'].forEach((v) {
products!.add(new Products.fromJson(v));
}); });
}
factory ProductResponse.fromJson(Map<String, dynamic> json) {
return ProductResponse(
success: json['success'],
totalData: json['total_data'],
totalPages: json['total_pages'],
product: (json['product'] as List)
.map((item) => Product.fromJson(item))
.toList(),
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { final Map<String, dynamic> data = new Map<String, dynamic>();
'success': success, data['success'] = this.success;
'total_data': totalData, data['total_data'] = this.totalData;
'total_pages': totalPages, data['total_pages'] = this.totalPages;
'product': product.map((item) => item.toJson()).toList(), if (this.products != null) {
}; data['products'] = this.products!.map((v) => v.toJson()).toList();
}
return data;
} }
} }
class Product { class Products {
final String id; String? sId;
final String SKU; String? sKU;
final String name; String? name;
final Category category; Category? category;
final double price; int? price;
final GST gst; GST? gST;
final String description; String? description;
final String specialInstructions; String? specialInstructions;
final String productStatus; String? productStatus;
final AddedBy addedBy; AddedBy? addedBy;
final List<dynamic> image; List<Null>? image;
final DateTime createdAt; String? createdAt;
final DateTime updatedAt; String? updatedAt;
final int v; int? iV;
Product({ Products(
required this.id, {this.sId,
required this.SKU, this.sKU,
required this.name, this.name,
required this.category, this.category,
required this.price, this.price,
required this.gst, this.gST,
required this.description, this.description,
required this.specialInstructions, this.specialInstructions,
required this.productStatus, this.productStatus,
required this.addedBy, this.addedBy,
required this.image, this.image,
required this.createdAt, this.createdAt,
required this.updatedAt, this.updatedAt,
required this.v, this.iV});
});
factory Product.fromJson(Map<String, dynamic> json) { Products.fromJson(Map<String, dynamic> json) {
return Product( sId = json['_id'];
id: json['_id'], sKU = json['SKU'];
SKU: json['SKU'], name = json['name'];
name: json['name'], category = json['category'] != null
category: Category.fromJson(json['category']), ? new Category.fromJson(json['category'])
price: (json['price'] as num).toDouble(), : null;
gst: GST.fromJson(json['GST']), price = json['price'];
description: json['description'], gST = json['GST'] != null ? new GST.fromJson(json['GST']) : null;
specialInstructions: json['special_instructions'], description = json['description'];
productStatus: json['product_Status'], specialInstructions = json['special_instructions'];
addedBy: AddedBy.fromJson(json['addedBy']), productStatus = json['product_Status'];
image: json['image'] as List<dynamic>, addedBy =
createdAt: DateTime.parse(json['createdAt']), json['addedBy'] != null ? new AddedBy.fromJson(json['addedBy']) : null;
updatedAt: DateTime.parse(json['updatedAt']), createdAt = json['createdAt'];
v: json['__v'], updatedAt = json['updatedAt'];
); iV = json['__v'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { final Map<String, dynamic> data = new Map<String, dynamic>();
'_id': id, data['_id'] = this.sId;
'SKU': SKU, data['SKU'] = this.sKU;
'name': name, data['name'] = this.name;
'category': category.toJson(), if (this.category != null) {
'price': price, data['category'] = this.category!.toJson();
'GST': gst.toJson(), }
'description': description, data['price'] = this.price;
'special_instructions': specialInstructions, if (this.gST != null) {
'product_Status': productStatus, data['GST'] = this.gST!.toJson();
'addedBy': addedBy.toJson(), }
'image': image, data['description'] = this.description;
'createdAt': createdAt.toIso8601String(), data['special_instructions'] = this.specialInstructions;
'updatedAt': updatedAt.toIso8601String(), data['product_Status'] = this.productStatus;
'__v': v, if (this.addedBy != null) {
}; data['addedBy'] = this.addedBy!.toJson();
}
data['createdAt'] = this.createdAt;
data['updatedAt'] = this.updatedAt;
data['__v'] = this.iV;
return data;
} }
} }
class Category { class Category {
final String id; String? sId;
final String categoryName; String? categoryName;
Category({ Category({this.sId, this.categoryName});
required this.id,
required this.categoryName,
});
factory Category.fromJson(Map<String, dynamic> json) { Category.fromJson(Map<String, dynamic> json) {
return Category( sId = json['_id'];
id: json['_id'], categoryName = json['categoryName'];
categoryName: json['categoryName'],
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { final Map<String, dynamic> data = new Map<String, dynamic>();
'_id': id, data['_id'] = this.sId;
'categoryName': categoryName, data['categoryName'] = this.categoryName;
}; return data;
} }
} }
class GST { class GST {
final String id; String? sId;
final String name; String? name;
final int tax; int? tax;
GST({ GST({this.sId, this.name, this.tax});
required this.id,
required this.name,
required this.tax,
});
factory GST.fromJson(Map<String, dynamic> json) { GST.fromJson(Map<String, dynamic> json) {
return GST( sId = json['_id'];
id: json['_id'], name = json['name'];
name: json['name'], tax = json['tax'];
tax: json['tax'],
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { final Map<String, dynamic> data = new Map<String, dynamic>();
'_id': id, data['_id'] = this.sId;
'name': name, data['name'] = this.name;
'tax': tax, data['tax'] = this.tax;
}; return data;
} }
} }
class AddedBy { class AddedBy {
final String id; String? sId;
final String name; String? name;
AddedBy({ AddedBy({this.sId, this.name});
required this.id,
required this.name,
});
factory AddedBy.fromJson(Map<String, dynamic> json) { AddedBy.fromJson(Map<String, dynamic> json) {
return AddedBy( sId = json['_id'];
id: json['_id'], name = json['name'];
name: json['name'],
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { final Map<String, dynamic> data = new Map<String, dynamic>();
'_id': id, data['_id'] = this.sId;
'name': name, data['name'] = this.name;
}; return data;
} }
} }

View File

@ -262,7 +262,7 @@ class CollectKycProvider extends ChangeNotifier {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull())); builder: (context) => const DataSubmitSuccessFullScreen()));
} }
} else { } else {
if (context.mounted) { if (context.mounted) {

View File

@ -0,0 +1,73 @@
import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/models/get_pd_rd_response.dart';
import 'package:cheminova/screens/Add_products_screen.dart';
import 'package:cheminova/services/api_client.dart';
import 'package:cheminova/services/api_urls.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class PdRdProvider extends ChangeNotifier {
String? selectedDistributorType;
bool _isLoading = false;
bool get isLoading => _isLoading;
List<GetPdRdResponse> _pdRdList = [];
List<GetPdRdResponse> get pdRdList => _pdRdList;
GetPdRdResponse? selectedPdRd;
final _apiClient = ApiClient();
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
Future<void> getPdRd() async {
setLoading(true);
try {
Response response = await _apiClient.get(
ApiUrls.getPdRdUrl + selectedDistributorType!.replaceAll(' ', ''));
setLoading(false);
if (response.statusCode == 200) {
_pdRdList = (response.data as List)
.map((json) => GetPdRdResponse.fromJson(json))
.toList();
notifyListeners();
} else {
debugPrint("Failed to load data: ${response.statusCode}");
}
} catch (e) {
debugPrint("Error occurred: $e");
} finally {
setLoading(false);
}
}
void updateDistributorType(String? val) {
selectedDistributorType = val;
selectedPdRd = null;
notifyListeners();
getPdRd();
}
updatePdRdValue(GetPdRdResponse? val) {
selectedPdRd = val;
notifyListeners();
Future.delayed(const Duration(milliseconds: 500), () {
if (selectedPdRd != null && selectedDistributorType != null) {
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => AddProductsScreen(
distributorType: selectedDistributorType!,
tradeName: selectedPdRd!.name ?? '',
pdRdId: selectedPdRd!.sId!)));
}
});
}
}

View File

@ -1,41 +1,125 @@
import 'dart:convert';
import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:cheminova/services/api_client.dart'; import 'package:cheminova/services/api_client.dart';
import 'package:cheminova/services/api_urls.dart'; import 'package:cheminova/services/api_urls.dart';
import 'package:flutter/cupertino.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../models/products_response.dart'; import '../models/products_response.dart';
class ProductProvider extends ChangeNotifier { class ProductProvider extends ChangeNotifier {
ProductProvider() {
getProducts();
}
final _apiClient = ApiClient(); final _apiClient = ApiClient();
ProductResponse? productResponse; ProductResponse? productResponse;
List<Product> productList = []; List<ProductModel> productList = [];
List<ProductModel> searchList = [];
bool _isLoading = false; bool _isLoading = false;
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
List<ProductModel> selectedProducts = [];
void setLoading(bool loading) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
notifyListeners(); notifyListeners();
} }
void filterProducts(String query) {
searchList = productList.where((product) {
final productNameLower = product.productName.toLowerCase();
final productSkuLower = product.sku.toLowerCase();
final searchLower = query.toLowerCase();
return productNameLower.contains(searchLower) ||
productSkuLower.contains(searchLower);
}).toList();
notifyListeners();
}
Future<void> getProducts() async { Future<void> getProducts() async {
setLoading(true); // setLoading(true);
try { // try {
Response response = await _apiClient.get(ApiUrls.getProducts); Response response = await _apiClient.get(ApiUrls.getProducts);
debugPrint('Response: $response');
setLoading(false); setLoading(false);
if (response.statusCode == 200) { if (response.statusCode == 200) {
productResponse = ProductResponse.fromJson(response.data); productResponse = ProductResponse.fromJson(response.data);
productList = productResponse!.product; productList = productResponse!.products!
.map((product) =>
ProductModel(sku: product.sKU!, productName: product.name!))
.toList();
notifyListeners(); notifyListeners();
} }
// } catch (e) {
// setLoading(false);
// debugPrint("Error: $e");
// }
}
Future<void> submitProducts(
{required String distributorType, required String pdRdId}) async {
setLoading(true);
try {
Response response = await _apiClient.post(ApiUrls.submitProductUrl,
data: json.encode({
"addedFor": distributorType.replaceAll(' ', ''),
"addedForId": pdRdId,
"products": selectedProducts.map((e) => e.toJson()).toList()
}));
setLoading(false);
if (response.statusCode == 201) {
ScaffoldMessenger.of(
navigatorKey.currentContext!,
).showSnackBar(
SnackBar(content: Text(response.data['message'])),
);
resetProducts();
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => const DataSubmitSuccessFullScreen()));
}
} catch (e) { } catch (e) {
setLoading(false); setLoading(false);
print("Error: $e"); debugPrint("Error: $e");
} }
} }
void resetProducts() {
selectedProducts.clear();
productList.clear();
productResponse = null;
notifyListeners();
}
}
class ProductModel {
String sku;
String productName;
int? sale;
int? inventory;
ProductModel(
{required this.sku,
required this.productName,
this.sale,
this.inventory});
factory ProductModel.fromJson(Map<String, dynamic> json) {
return ProductModel(
sku: json['SKU'],
productName: json['ProductName'],
sale: json['Sale'],
inventory: json['Inventory']);
}
Map<String, dynamic> toJson() {
return {
'SKU': sku,
'ProductName': productName,
'Sale': sale,
'Inventory': inventory
};
}
} }

View File

@ -1,99 +1,83 @@
import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../models/products_response.dart';
import '../provider/products_provider.dart'; import '../provider/products_provider.dart';
class AddProductsScreen extends StatefulWidget { class AddProductsScreen extends StatefulWidget {
const AddProductsScreen({super.key}); final String distributorType;
final String tradeName;
final String pdRdId;
const AddProductsScreen({
super.key,
required this.distributorType,
required this.tradeName,
required this.pdRdId
});
@override @override
State<AddProductsScreen> createState() => _AddProductsScreenState(); State<AddProductsScreen> createState() => _AddProductsScreenState();
} }
class _AddProductsScreenState extends State<AddProductsScreen> { class _AddProductsScreenState extends State<AddProductsScreen> {
List<Product> selectedProducts = [];
List<Product> filteredProducts = [];
final searchController = TextEditingController(); final searchController = TextEditingController();
late ProductProvider provider; late ProductProvider productProvider;
@override @override
void initState() { void initState() {
productProvider = Provider.of<ProductProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
productProvider.getProducts();
});
super.initState(); super.initState();
provider=Provider.of<ProductProvider>(context,listen: false);
provider.getProducts();
filteredProducts = provider.productList;
} }
void filterProducts(String query) { @override
setState(() { void dispose() {
final provider = Provider.of<ProductProvider>(context, listen: false); if (mounted) {
filteredProducts = provider.productList.where((product) { productProvider.resetProducts();
final productNameLower = product.name.toLowerCase(); }
final productSkuLower = product.SKU.toLowerCase(); super.dispose();
final searchLower = query.toLowerCase();
return productNameLower.contains(searchLower) ||
productSkuLower.contains(searchLower);
}).toList();
});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground(
return
CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () => Navigator.pop(context),
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20)
), )
], ],
title: const Text('Add Products', title: Text(
style: TextStyle( '${widget.distributorType}\n${widget.tradeName}',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek')), fontFamily: 'Anek'
)
),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
body: Consumer<ProductProvider>( bottomNavigationBar: Consumer<ProductProvider>(
builder: (context, provider, child) { builder: (context, value, child) => Column(
if (provider.isLoading) { mainAxisSize: MainAxisSize.min,
return const Center(child: CircularProgressIndicator());
}
return Stack(
children: [ children: [
Column(
children: [
if (selectedProducts.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: selectedProducts.length,
itemBuilder: (context, index) {
return ProductBlock(
product: selectedProducts[index]);
},
),
),
],
),
Align( Align(
alignment: selectedProducts.isEmpty alignment: value.selectedProducts.isEmpty
? Alignment.center ? Alignment.center
: Alignment.bottomCenter, : Alignment.bottomCenter,
child: Padding( child: Padding(
@ -106,12 +90,12 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
showModalBottomSheet( showModalBottomSheet(
isScrollControlled: true, isScrollControlled: true,
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: maxHeight: MediaQuery.of(context).size.height * 0.9
MediaQuery.of(context).size.height * 0.9,
), ),
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return Consumer<ProductProvider>(builder: (context, value, child) =>StatefulBuilder( return Consumer<ProductProvider>(
builder: (context, value, child) => StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
return Column( return Column(
children: [ children: [
@ -120,116 +104,158 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
child: TextField( child: TextField(
controller: searchController, controller: searchController,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: labelText: 'Search by name or SKU',
'Search by name or SKU',
border: OutlineInputBorder(), border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search)
), ),
onChanged: (value) { onChanged: (val) {
filterProducts(value); value.filterProducts(val);
setState(() {}); setState(() {});
}, }
), )
), ),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: filteredProducts.length, itemCount: searchController.text.isEmpty
? value.productList.length
: value.searchList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
bool isAlreadySelected = bool isAlreadySelected = value.selectedProducts.any(
selectedProducts.contains( (selectedProduct) => selectedProduct.sku == value.productList[index].sku
filteredProducts[index]); );
final data = searchController.text.isEmpty
? value.productList[index]
: value.searchList[index];
return Card( return Card(
child: ListTile( child: ListTile(
title: Text( title: Text(
filteredProducts[index] data.productName,
.name, style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black)
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors.black,
),
), ),
subtitle: Text( subtitle: Text(
filteredProducts[index].SKU, data.sku,
style: TextStyle( style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black)
color: isAlreadySelected
? Colors.grey
: Colors.black,
),
), ),
onTap: isAlreadySelected onTap: isAlreadySelected
? null ? null
: () { : () {
setState(() { setState(() => value.selectedProducts.add(data));
selectedProducts.add( Navigator.pop(context);
filteredProducts[ }
index]); )
}); );
Navigator.pop( }
context); )
}, )
), ]
);
}
)
); );
}, },
), ).whenComplete(() => setState(() {}));
),
],
);
},
),
);
},
).whenComplete(() {
setState(() {});
});
}, },
backgroundColor: Colors.white, backgroundColor: Colors.white,
icon: const Icon(Icons.add, color: Colors.black), icon: const Icon(Icons.add, color: Colors.black),
label: const Text( label: const Text('Add Products', style: TextStyle(color: Colors.black))
'Add Products',
style: TextStyle(color: Colors.black),
), ),
), if (value.selectedProducts.isNotEmpty) ...[
if (selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0), const SizedBox(height: 16.0),
CommonElevatedButton( Consumer<ProductProvider>(
builder: (context, value, child) => CommonElevatedButton(
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'SUBMIT', text: 'SUBMIT',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
Navigator.push( if (value.selectedProducts.isNotEmpty &&
context, value.selectedProducts.every((product) =>
MaterialPageRoute( product.sku.isNotEmpty &&
builder: (context) => product.productName.isNotEmpty &&
const DataSubmitSuccessfull(), product.sale != null &&
product.inventory != null)) {
value.submitProducts(
distributorType: widget.distributorType,
pdRdId: widget.pdRdId
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please fill out all product details, including sale and inventory.')
), ),
); );
}
}
)
)
]
]
)
)
),
],
),
),
body: Consumer<ProductProvider>(
builder: (context, value, child) {
return Stack(
children: [
Column(
children: [
if (value.selectedProducts.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: value.selectedProducts.length,
itemBuilder: (context, index) {
return ProductBlock(
onUpdate: (updatedProduct) {
debugPrint('selected productt le ${value.selectedProducts.length}');
setState(() {
value.selectedProducts[index] = updatedProduct;
});
}, },
), onRemove: () {
], setState(() {
], value.selectedProducts.removeAt(index);
), });
),
),
],
);
}, },
), product: value.selectedProducts[index]
), );
}
)
)
]
),
(value.isLoading)
? Container(
color: Colors.black12,
child: const Center(child: CircularProgressIndicator())
)
: const SizedBox()
]
);
}
)
)
); );
} }
} }
class ProductBlock extends StatefulWidget { class ProductBlock extends StatefulWidget {
final Product product; final ProductModel product;
final ValueChanged<ProductModel> onUpdate;
final VoidCallback onRemove;
const ProductBlock({super.key, required this.product}); const ProductBlock({
super.key,
required this.product,
required this.onUpdate,
required this.onRemove,
});
@override @override
_ProductBlockState createState() => _ProductBlockState(); State<ProductBlock> createState() => _ProductBlockState();
} }
class _ProductBlockState extends State<ProductBlock> { class _ProductBlockState extends State<ProductBlock> {
@ -240,21 +266,36 @@ class _ProductBlockState extends State<ProductBlock> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
saleController.text = (widget.product.sale ?? '').toString();
inventoryController.text = (widget.product.inventory ?? '').toString();
} }
void validateInput() { void validateInput() {
setState(() { setState(() {
if (saleController.text.isNotEmpty && String? saleError;
inventoryController.text.isNotEmpty) { String? inventoryError;
if (saleController.text.isEmpty) {
saleError = 'Sale cannot be empty.';
}
if (inventoryController.text.isEmpty) {
inventoryError = 'Inventory cannot be empty.';
}
errorMessage = null;
if (saleError == null && inventoryError == null) {
int sale = int.parse(saleController.text); int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text); int inventory = int.parse(inventoryController.text);
if (inventory > sale) {
errorMessage = 'Inventory should be less than or equal to sales'; widget.onUpdate(ProductModel(
sku: widget.product.sku,
productName: widget.product.productName,
sale: sale,
inventory: inventory,
));
} else { } else {
errorMessage = null; errorMessage = saleError ?? inventoryError;
}
} else {
errorMessage = null;
} }
}); });
} }
@ -262,46 +303,66 @@ class _ProductBlockState extends State<ProductBlock> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
// color: !widget.product.isPurchased ? Colors.white54 : Colors.white,
color: Colors.white, color: Colors.white,
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
child: Padding( child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Product: ${widget.product.name}', Text('Product: ${widget.product.productName}',
style: const TextStyle(fontSize: 16)), style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.SKU}', Text('SKU: ${widget.product.sku}',
style: const TextStyle(fontSize: 15)), style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8), const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField( TextField(
controller: saleController, controller: saleController,
decoration: const InputDecoration(labelText: 'Sale'), onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Sale',
errorText: saleController.text.isEmpty
? 'Sale cannot be empty.'
: null
),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
// enabled: widget.product.isPurchased,
enabled: true, enabled: true,
onChanged: (_) => validateInput(), onChanged: (_) => validateInput()
), ),
TextField( TextField(
controller: inventoryController, controller: inventoryController,
decoration: const InputDecoration(labelText: 'Inventory'), onTapOutside: (event) => FocusScope.of(context).unfocus(),
keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly],
// enabled: widget.product.isPurchased, decoration: InputDecoration(
enabled: true, labelText: 'Inventory',
onChanged: (_) => validateInput(), errorText: inventoryController.text.isEmpty
? 'Inventory cannot be empty.'
: null
), ),
if (errorMessage != null) keyboardType: TextInputType.number,
Padding( enabled: true,
padding: const EdgeInsets.only(top: 8.0), onChanged: (_) => validateInput()
child: Text( )
errorMessage!, ]
style: const TextStyle(color: Colors.red), )
]
),
),
Positioned(
top: 4,
right: 4,
child: IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
onPressed: widget.onRemove,
), ),
), ),
], ],
), )
),
); );
} }
} }

View File

@ -2,14 +2,14 @@ import 'package:cheminova/screens/home_screen.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DataSubmitSuccessfull extends StatefulWidget { class DataSubmitSuccessFullScreen extends StatefulWidget {
const DataSubmitSuccessfull({super.key}); const DataSubmitSuccessFullScreen({super.key});
@override @override
State<DataSubmitSuccessfull> createState() => DataSubmitSuccessfullState(); State<DataSubmitSuccessFullScreen> createState() => DataSubmitSuccessFullScreenState();
} }
class DataSubmitSuccessfullState extends State<DataSubmitSuccessfull> { class DataSubmitSuccessFullScreenState extends State<DataSubmitSuccessFullScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();

View File

@ -118,7 +118,7 @@ class DisplaySalesScreenState extends State<DisplaySalesScreen> {
text: 'SUBMIT', text: 'SUBMIT',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessfull())); Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen()));
}) })

View File

@ -30,15 +30,16 @@ class _HomePageState extends State<HomePage> {
@override @override
void initState() { void initState() {
Provider.of<HomeProvider>(context, listen: false).getProfile();
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<HomeProvider>(context, listen: false).getProfile();
notificationServices.requestNotificationPermission(); notificationServices.requestNotificationPermission();
notificationServices.getDeviceToken().then((value) { notificationServices.getDeviceToken().then((value) {
if (kDebugMode) { if (kDebugMode) {
print('Device Token: $value'); print('Device Token: $value');
} }
}); });
});
} }
@override @override

View File

@ -99,9 +99,7 @@ class ProductSalesDataState extends State<ProductSalesData> {
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'VIEW DATA', text: 'VIEW DATA',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen(),)),
Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessfull(),));
},
) )

View File

@ -108,7 +108,7 @@ class SummaryScreenState extends State<SummaryScreen> {
text: 'VIEW DATA', text: 'VIEW DATA',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessfull())); Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen()));
}) })
), ),

View File

@ -1,10 +1,10 @@
import 'package:cheminova/screens/Add_products_screen.dart'; import 'package:cheminova/models/get_pd_rd_response.dart';
import 'package:flutter/material.dart'; import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_app_bar.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_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:flutter/material.dart';
import 'package:cheminova/screens/data_submit_successfull.dart'; import 'package:provider/provider.dart';
class UpdateInventoryScreen extends StatefulWidget { class UpdateInventoryScreen extends StatefulWidget {
const UpdateInventoryScreen({super.key}); const UpdateInventoryScreen({super.key});
@ -14,53 +14,27 @@ class UpdateInventoryScreen extends StatefulWidget {
} }
class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> { class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
final List<Product> products = [ late PdRdProvider pdRdProvider;
Product(name: 'Product A', sku: 'SKU001', isPurchased: true),
Product(name: 'Product B', sku: 'SKU002', isPurchased: true),
Product(name: 'Product C', sku: 'SKU003', isPurchased: false),
];
List<Product> selectedProducts = [];
List<Product> filteredProducts = [];
final List<String> principalDistributors = ['vaibhav', 'sonu', 'monu'];
final List<String> retailerDistributors = ['shivam', 'vivek'];
String? selectedDistributorType;
String? selectedDistributor;
final searchController = TextEditingController();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
filteredProducts = products; pdRdProvider = PdRdProvider();
}
void filterProducts(String query) {
setState(() {
filteredProducts = products.where((product) {
final productNameLower = product.name.toLowerCase();
final productSkuLower = product.sku.toLowerCase();
final searchLower = query.toLowerCase();
return productNameLower.contains(searchLower) ||
productSkuLower.contains(searchLower);
}).toList();
});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( return ChangeNotifierProvider(
create: (context) => pdRdProvider,
child: CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () => Navigator.pop(context),
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20))
),
], ],
title: const Text('Update Inventory Data', title: const Text('Update Inventory Data',
style: TextStyle( style: TextStyle(
@ -69,105 +43,60 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek')), fontFamily: 'Anek')),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0),
),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
bottomNavigationBar: Padding( body: Stack(children: [
padding: const EdgeInsets.all(16.0), Consumer<PdRdProvider>(
child: CommonElevatedButton( builder: (context, value, child) => Column(children: [
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddProductsScreen(),
),
);
},
),
),
body: Stack(
children: [
Column(
children: [
// Dropdown for selecting distributor type
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25), padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Select Distributor Type',
fillColor: Colors.white, fillColor: Colors.white,
filled: true, filled: true,
border: OutlineInputBorder(), border: OutlineInputBorder()),
), value: value.selectedDistributorType,
value: selectedDistributorType, items: [
items: ['Principal Distributor', 'Retailer Distributor'].map((String type) { 'Principal Distributor',
'Retail Distributor'
].map((String type) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: type, value: type, child: Text(type));
child: Text(type),
);
}).toList(), }).toList(),
onChanged: (value) { hint: const Text('Select Distributor Type'),
setState(() { onChanged: (val) =>
selectedDistributorType = value; value.updateDistributorType(val))),
selectedDistributor = null; // Reset distributor selection when type changes
});
},
),
),
// Dropdown for selecting distributor name based on type
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25), padding: const EdgeInsets.symmetric(
child: DropdownButtonFormField<String>( horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<GetPdRdResponse>(
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Select Distributor Name',
fillColor: Colors.white, fillColor: Colors.white,
filled: true, filled: true,
border: OutlineInputBorder(), border: OutlineInputBorder()),
), value: value.selectedPdRd,
value: selectedDistributor, items: value.pdRdList.map((e) {
items: (selectedDistributorType == 'Principal Distributor' return DropdownMenuItem<GetPdRdResponse>(
? principalDistributors value: e, child: Text(e.name ?? ''));
: retailerDistributors)
.map((String distributor) {
return DropdownMenuItem<String>(
value: distributor,
child: Text(distributor),
);
}).toList(), }).toList(),
onChanged: (value) { onChanged: (val) =>
setState(() { value.updatePdRdValue(val),
selectedDistributor = value;
});
},
// Disable the dropdown if no distributor type is selected // Disable the dropdown if no distributor type is selected
isExpanded: true, isExpanded: true,
isDense: true, isDense: true,
iconSize: 24, iconSize: 24,
hint: Text( hint:
'Please select a ${selectedDistributorType ?? "Distributor Type"} first'), const Text('Select Distributor Name')))
), ])),
), Consumer<PdRdProvider>(
// Show the selected products builder: (context, value, child) => value.isLoading
if (selectedProducts.isNotEmpty) ? Container(
Expanded( color: Colors.black12,
child: ListView.builder( child: const Center(
itemCount: selectedProducts.length, child: CircularProgressIndicator()))
itemBuilder: (context, index) { : const SizedBox())
return ProductBlock(product: selectedProducts[index]); ]))));
},
),
),
],
),
],
),
),
);
} }
} }
@ -210,7 +139,8 @@ class _ProductBlockState extends State<ProductBlock> {
void validateInput() { void validateInput() {
setState(() { setState(() {
if (saleController.text.isNotEmpty && inventoryController.text.isNotEmpty) { if (saleController.text.isNotEmpty &&
inventoryController.text.isNotEmpty) {
int sale = int.parse(saleController.text); int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text); int inventory = int.parse(inventoryController.text);
if (inventory > sale) { if (inventory > sale) {
@ -234,8 +164,10 @@ class _ProductBlockState extends State<ProductBlock> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Product: ${widget.product.name}', style: const TextStyle(fontSize: 16)), Text('Product: ${widget.product.name}',
Text('SKU: ${widget.product.sku}', style: const TextStyle(fontSize: 15)), style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.sku}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8), const SizedBox(height: 8),
TextField( TextField(
controller: saleController, controller: saleController,

View File

@ -1,6 +1,7 @@
import 'package:cheminova/services/secure__storage_service.dart'; import 'package:cheminova/services/secure__storage_service.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:cheminova/services/api_urls.dart'; import 'package:cheminova/services/api_urls.dart';
import 'package:flutter/material.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class ApiClient { class ApiClient {
@ -19,6 +20,7 @@ class ApiClient {
.add(InterceptorsWrapper(onRequest: (options, handler) async { .add(InterceptorsWrapper(onRequest: (options, handler) async {
String? token = await _storageService.read(key: 'access_token'); String? token = await _storageService.read(key: 'access_token');
if (token != null) { if (token != null) {
debugPrint('Token start ------------> $token <------------ Token end');
options.headers['Authorization'] = 'Bearer $token'; options.headers['Authorization'] = 'Bearer $token';
} }
return handler.next(options); return handler.next(options);

View File

@ -14,5 +14,6 @@ class ApiUrls {
static const String notificationUrl = '${baseUrl}/get-notification-sc'; static const String notificationUrl = '${baseUrl}/get-notification-sc';
static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc'; static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc';
static const String getProducts = '${baseUrl}product/getAll/user'; static const String getProducts = '${baseUrl}product/getAll/user';
static const String getPdRdUrl = '${baseUrl}inventory/distributors-SC/';
static const String submitProductUrl = '${baseUrl}inventory/add-SC';
} }