From 5326f4bd86a4c28367821bc99486dd2e0fdfca78 Mon Sep 17 00:00:00 2001 From: kratikpal Date: Wed, 11 Sep 2024 16:56:08 +0530 Subject: [PATCH] Update sales data task --- lib/constants/constant.dart | 2 + lib/main.dart | 4 + lib/models/pd_rd_response_model.dart | 57 ++- lib/models/sales_task_response.dart | 174 +++++++ lib/provider/add_sales_provider.dart | 126 +++++ lib/provider/collect_kyc_provider.dart | 17 +- lib/provider/task_provider.dart | 4 +- lib/screens/add_products_screen.dart | 7 +- lib/screens/add_sales_products_screen.dart | 463 ++++++++++++++++++ .../assign_task_dash_board_screen.dart | 18 +- lib/screens/assign_tasks_screen.dart | 28 +- lib/screens/daily_tasks_screen.dart | 50 +- lib/screens/home_screen.dart | 55 +-- lib/screens/rejected_application_screen.dart | 148 +++--- lib/screens/retailer_details_screen.dart | 18 + ...en.dart => select_distributer_screen.dart} | 49 +- lib/screens/task_management_screen.dart | 25 +- .../verification_documents_screen.dart | 1 + lib/services/api_urls.dart | 5 +- lib/utils/string_extension.dart | 5 + lib/widgets/common_text_form_field.dart | 3 + 21 files changed, 1081 insertions(+), 178 deletions(-) create mode 100644 lib/models/sales_task_response.dart create mode 100644 lib/provider/add_sales_provider.dart create mode 100644 lib/screens/add_sales_products_screen.dart rename lib/screens/{update_inventory_screen.dart => select_distributer_screen.dart} (77%) create mode 100644 lib/utils/string_extension.dart diff --git a/lib/constants/constant.dart b/lib/constants/constant.dart index 1d6839a..f53f43c 100644 --- a/lib/constants/constant.dart +++ b/lib/constants/constant.dart @@ -8,3 +8,5 @@ final GlobalKey _scaffoldMessengerKey = GlobalKey get scaffoldMessengerKey => _scaffoldMessengerKey; + + GlobalKey navigatorKey = GlobalKey(); \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 8a6887e..cdeca31 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'dart:developer'; import 'dart:io'; +import 'package:cheminova/constants/constant.dart'; +import 'package:cheminova/provider/add_sales_provider.dart'; import 'package:cheminova/provider/collect_kyc_provider.dart'; import 'package:cheminova/provider/pd_rd_provider.dart'; import 'package:cheminova/provider/product_manual_provider.dart'; @@ -106,6 +108,7 @@ Future main() async { ChangeNotifierProvider(create: (_) => PdRdProvider()), ChangeNotifierProvider(create: (_) => TaskProvider()), ChangeNotifierProvider(create: (_) => ProductManualProvider()), + ChangeNotifierProvider(create: (_) => AddSalesProvider()), ], child: const MyApp(), ), @@ -123,6 +126,7 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( + navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, // scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey, title: 'cheminova', diff --git a/lib/models/pd_rd_response_model.dart b/lib/models/pd_rd_response_model.dart index cd8f776..2979b26 100644 --- a/lib/models/pd_rd_response_model.dart +++ b/lib/models/pd_rd_response_model.dart @@ -2,15 +2,16 @@ class PdRdResponseModel { String? id; String? uniqueId; String? name; - String? tradeNameRd; ShippingAddress? shippingAddress; + Kyc? kyc; + String? salesCoordinator; // Nullable property for SalesCoordinator PdRdResponseModel({ this.id, this.uniqueId, this.name, - this.tradeNameRd, + this.kyc, this.shippingAddress, this.salesCoordinator, // Initialize SalesCoordinator }); @@ -20,7 +21,7 @@ class PdRdResponseModel { id: json["_id"], name: json["name"], uniqueId: json["uniqueId"], - tradeNameRd: json["trade_name"], + kyc: json["kyc"] != null ? Kyc.fromJson(json["kyc"]) : null, shippingAddress: json['shippingAddress'] != null ? ShippingAddress.fromJson(json['shippingAddress']) : null, @@ -70,3 +71,53 @@ class ShippingAddress { ); } } + +class Kyc { + final String id; + final String street; + final String city; + final String state; + final String postalCode; + final String country; + final String panNumber; + final String tradeName; + final String gstNumber; + final bool isDefault; + final String? panImgUrl; // New field for PAN image URL + final String? aadharImgUrl; // New field for Aadhar image URL + final String? gstImgUrl; // New field for GST image URL + + Kyc({ + required this.id, + required this.street, + required this.city, + required this.state, + required this.postalCode, + required this.country, + required this.panNumber, + required this.tradeName, + required this.gstNumber, + required this.isDefault, + this.panImgUrl, + this.aadharImgUrl, + this.gstImgUrl, + }); + + factory Kyc.fromJson(Map json) { + return Kyc( + id: json['_id'] ?? '', + street: json['street'] ?? '', + city: json['city'] ?? '', + state: json['state'] ?? '', + postalCode: json['postalCode'] ?? '', + country: json['country'] ?? '', + panNumber: json['panNumber'] ?? '', + tradeName: json['trade_name'] ?? '', + gstNumber: json['gstNumber'] ?? '', + isDefault: json['isDefault'] ?? false, + panImgUrl: json['pan_img']?['url'], + aadharImgUrl: json['aadhar_img']?['url'], + gstImgUrl: json['gst_img']?['url'], + ); + } +} diff --git a/lib/models/sales_task_response.dart b/lib/models/sales_task_response.dart new file mode 100644 index 0000000..95748f1 --- /dev/null +++ b/lib/models/sales_task_response.dart @@ -0,0 +1,174 @@ +class SalesTaskResponse { + bool? success; + int? totalData; + int? totalPages; + List? products; + + SalesTaskResponse( + {this.success, this.totalData, this.totalPages, this.products}); + + SalesTaskResponse.fromJson(Map json) { + success = json['success']; + totalData = json['total_data']; + totalPages = json['total_pages']; + if (json['products'] != null) { + products = []; + json['products'].forEach((v) { + products!.add(SalesProduct.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['success'] = success; + data['total_data'] = totalData; + data['total_pages'] = totalPages; + if (products != null) { + data['products'] = products!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class SalesProduct { + String? sId; + String? SKU; + String? ProductName; + Category? category; + Brand? brand; + int? price; + int? gST; + int? hSNCode; + String? description; + String? productStatus; + AddedBy? addedBy; + List? image; + String? createdAt; + String? updatedAt; + int? SalesAmount; + int? QuantitySold; + String? comments; + int? iV; + + SalesProduct( + {this.sId, + this.SKU, + this.ProductName, + this.category, + this.brand, + this.price, + this.gST, + this.hSNCode, + this.description, + this.productStatus, + this.addedBy, + this.image, + this.createdAt, + this.updatedAt, + this.QuantitySold, + this.comments, + this.SalesAmount, + this.iV}); + + SalesProduct.fromJson(Map json) { + sId = json['_id']; + SKU = json['SKU']; + ProductName = json['name']; + category = + json['category'] != null ? Category.fromJson(json['category']) : null; + brand = json['brand'] != null ? Brand.fromJson(json['brand']) : null; + price = json['price']; + gST = json['GST']; + hSNCode = json['HSN_Code']; + description = json['description']; + productStatus = json['product_Status']; + addedBy = + json['addedBy'] != null ? AddedBy.fromJson(json['addedBy']) : null; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + iV = json['__v']; + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['SKU'] = SKU; + data['name'] = ProductName; + if (category != null) { + data['category'] = category!.toJson(); + } + if (brand != null) { + data['brand'] = brand!.toJson(); + } + data['price'] = price; + data['GST'] = gST; + data['HSN_Code'] = hSNCode; + data['description'] = description; + data['product_Status'] = productStatus; + if (addedBy != null) { + data['addedBy'] = addedBy!.toJson(); + } + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = iV; + return data; + } +} + +class Category { + String? sId; + String? categoryName; + + Category({this.sId, this.categoryName}); + + Category.fromJson(Map json) { + sId = json['_id']; + categoryName = json['categoryName']; + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['categoryName'] = categoryName; + return data; + } +} + +class Brand { + String? sId; + String? brandName; + + Brand({this.sId, this.brandName}); + + Brand.fromJson(Map json) { + sId = json['_id']; + brandName = json['brandName']; + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['brandName'] = brandName; + return data; + } +} + +class AddedBy { + String? sId; + String? name; + + AddedBy({this.sId, this.name}); + + AddedBy.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['name'] = name; + return data; + } +} diff --git a/lib/provider/add_sales_provider.dart b/lib/provider/add_sales_provider.dart new file mode 100644 index 0000000..5f035f5 --- /dev/null +++ b/lib/provider/add_sales_provider.dart @@ -0,0 +1,126 @@ +import 'dart:convert'; + +import 'package:cheminova/constants/constant.dart'; +import 'package:cheminova/models/sales_task_response.dart'; +import 'package:cheminova/screens/data_submit_successfull.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 AddSalesProvider with ChangeNotifier { + final _apiClient = ApiClient(); + List tasksList = []; + List searchList = []; + + bool _isLoading = false; + + bool get isLoading => _isLoading; + + List selectedProducts = []; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + Future getTask() async { + setLoading(true); + try { + Response response = await _apiClient.get(ApiUrls.salesTaskUrl); + setLoading(false); + if (response.statusCode == 200) { + final data = SalesTaskResponse.fromJson(response.data); + tasksList = data.products ?? []; + notifyListeners(); + } + } catch (e) { + setLoading(false); + debugPrint("Error occurred while fetching sales tasks: $e"); + } + } + + void filterProducts(String val) { + tasksList = tasksList.where((element) { + final productNameLower = element.ProductName!.toLowerCase(); + final productSkuLower = element.SKU!.toLowerCase(); + final searchLower = val.toLowerCase(); + return productNameLower.contains(searchLower) || + productSkuLower.contains(searchLower); + }).toList(); + notifyListeners(); + } + + Future submitProducts( + {required String distributorType, + required String pdRdId, + required String date, + String? inventoryId, + required String tradeName}) async { + setLoading(true); + try { + Response response = await _apiClient.post(ApiUrls.postSalesTaskUrl, + data: json.encode({ + "addedFor": distributorType.replaceAll(' ', ''), + "addedForId": pdRdId, + "tradename": tradeName, + "date": date, + + "products": selectedProducts.map((product) { + return { + "SKU": product.SKU, + "ProductName": product.ProductName, + "QuantitySold": product.QuantitySold, + "comments": product.comments, + "SalesAmount": product.SalesAmount + }; + }).toList() + // "products": selectedProducts.map((e) => e.toJson()).toList() + })); + setLoading(false); + if (response.statusCode == 201) { + ScaffoldMessenger.of( + navigatorKey.currentContext!, + ).showSnackBar( + SnackBar(content: Text(response.data['message'])), + ); + if (inventoryId != null) { + _apiClient + .put(ApiUrls.updateTaskInventoryUrl + inventoryId, data: null) + .then((value) { + debugPrint('Task Updated'); + if (value.statusCode == 200) { + resetProducts(); + Navigator.push( + navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) => + const DataSubmitSuccessfull())); + } else { + ScaffoldMessenger.of( + navigatorKey.currentContext!, + ).showSnackBar( + const SnackBar(content: Text('Task not updated')), + ); + } + }); + } else { + resetProducts(); + Navigator.push( + navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) => const DataSubmitSuccessfull())); + } + } + } catch (e) { + setLoading(false); + debugPrint("Error: $e"); + } + } + + void resetProducts() { + selectedProducts.clear(); + tasksList.clear(); + notifyListeners(); + } +} \ No newline at end of file diff --git a/lib/provider/collect_kyc_provider.dart b/lib/provider/collect_kyc_provider.dart index fd09b7c..a750205 100644 --- a/lib/provider/collect_kyc_provider.dart +++ b/lib/provider/collect_kyc_provider.dart @@ -30,6 +30,7 @@ class CollectKycProvider extends ChangeNotifier { final aadharNumberController = TextEditingController(); final panNumberController = TextEditingController(); final gstNumberController = TextEditingController(); + final emailController = TextEditingController(); String? selectedDistributor; @@ -210,6 +211,12 @@ class CollectKycProvider extends ChangeNotifier { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Selfie of Entrance Board is required')), ); + } else if (emailController.text.trim().isEmpty || + !emailController.text.trim().contains(RegExp( + r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+"))) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Invalid email address')), + ); } else { submitCollectKycForm(context); } @@ -221,6 +228,7 @@ class CollectKycProvider extends ChangeNotifier { 'name': nameController.text.trim(), 'trade_name': tradeNameController.text.trim(), 'address': addressController.text.trim(), + 'email': emailController.text.trim(), 'state': state.text.trim(), 'city': city.text.trim(), 'district': districtController.text.trim(), @@ -267,8 +275,13 @@ class CollectKycProvider extends ChangeNotifier { } } else { if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('Submission failed: ${response.statusMessage}'))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '${response.data['message'] ?? 'Something went wrong'}', + ), + ), + ); } } } catch (e) { diff --git a/lib/provider/task_provider.dart b/lib/provider/task_provider.dart index 8b61b90..d34602c 100644 --- a/lib/provider/task_provider.dart +++ b/lib/provider/task_provider.dart @@ -112,7 +112,7 @@ class TaskProvider extends ChangeNotifier { 'addedForId': _selectedDistributor!.id, 'tradename': selectedDistributorType == 'PrincipalDistributor' ? _selectedDistributor!.shippingAddress!.tradeName - : _selectedDistributor!.tradeNameRd, + : _selectedDistributor!.kyc!.tradeName, }); } @@ -180,7 +180,7 @@ class TaskProvider extends ChangeNotifier { .map((json) => PdRdResponseModel.fromJson(json)) .toList(); _rdList = data; - print("RDTradeName ${data[0].tradeNameRd}"); + print("RDTradeName ${data[0].kyc!.tradeName}"); } else { print("Failed to load data: ${response.statusCode}"); } diff --git a/lib/screens/add_products_screen.dart b/lib/screens/add_products_screen.dart index f94df2e..e2ab036 100644 --- a/lib/screens/add_products_screen.dart +++ b/lib/screens/add_products_screen.dart @@ -2,6 +2,7 @@ import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/models/product_model.dart'; import 'package:cheminova/provider/product_provider.dart'; import 'package:cheminova/screens/data_submit_successfull.dart'; +import 'package:cheminova/utils/string_extension.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_drawer.dart'; @@ -72,7 +73,7 @@ class _AddProductsScreenState extends State { ), ], title: Text( - widget.distributor.name!, + widget.distributor.name!.capitalize(), style: const TextStyle( fontSize: 20, color: Colors.black, @@ -160,7 +161,7 @@ class _AddProductsScreenState extends State { child: ListTile( title: Text( filteredProducts[index] - .name, + .name.capitalize(), style: TextStyle( color: isAlreadySelected ? Colors.grey @@ -308,7 +309,7 @@ class _ProductBlockState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Product: ${provider.selectedProducts[widget.index].name}', + Text('Product: ${provider.selectedProducts[widget.index].name.capitalize()}', style: const TextStyle(fontSize: 16)), Text('SKU: ${provider.selectedProducts[widget.index].SKU}', style: const TextStyle(fontSize: 15)), diff --git a/lib/screens/add_sales_products_screen.dart b/lib/screens/add_sales_products_screen.dart new file mode 100644 index 0000000..b8c9e94 --- /dev/null +++ b/lib/screens/add_sales_products_screen.dart @@ -0,0 +1,463 @@ +import 'package:cheminova/models/sales_task_response.dart'; +import 'package:cheminova/provider/add_sales_provider.dart'; +import 'package:cheminova/utils/string_extension.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 createState() => _AddSalesProductScreenState(); +} + +class _AddSalesProductScreenState extends State { + final searchController = TextEditingController(); + late AddSalesProvider salesTaskProvider; + final dateController = TextEditingController(); + final formKey = GlobalKey(); + + @override + void initState() { + salesTaskProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + salesTaskProvider.getTask(); + }); + super.initState(); + } + + @override + void dispose() { + if (mounted) { + salesTaskProvider.resetProducts(); + } + super.dispose(); + } + + datePicker() async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2101), + ); + if (picked != null) { + setState( + () => dateController.text = DateFormat('dd/MM/yyyy').format(picked)); + } + } + + @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.capitalize()}', + 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( + 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: () { + showModalBottomSheet( + isScrollControlled: true, + constraints: BoxConstraints( + maxHeight: + MediaQuery.of(context).size.height * + 0.9), + context: context, + builder: (BuildContext context) { + return Consumer( + 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) { + bool isAlreadySelected = value + .selectedProducts + .any((selectedProduct) => + selectedProduct.SKU == + value.tasksList[index] + .SKU); + final data = searchController + .text.isEmpty + ? value.tasksList[index] + : value.searchList[index]; + return Card( + child: ListTile( + title: Text( + data.ProductName + ?.capitalize() ?? + '', + 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(() {})); + }, + 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), + Consumer( + builder: (context, value, child) => + CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SUBMIT', + backgroundColor: const Color(0xff004791), + onPressed: () { + 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.', + ), + ), + ); + } + } + }, + ), + ), + ] + ], + ), + ), + ), + ], + ), + ), + body: Consumer( + builder: (context, value, child) { + return Stack( + children: [ + Column( + children: [ + GestureDetector( + onTap: () => datePicker(), + child: AbsorbPointer( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + key: formKey, + child: TextFormField( + controller: dateController, + validator: (value) { + if (value!.isEmpty) { + return 'Please select a date'; + } + return null; + }, + 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]); + }, + ), + ) + ], + ), + (value.isLoading) + ? Container( + color: Colors.black12, + child: const Center( + child: CircularProgressIndicator(), + ), + ) + : const SizedBox() + ], + ); + }, + ), + ), + ), + ); + } +} + +class ProductBlock extends StatefulWidget { + final SalesProduct product; + final ValueChanged onUpdate; + final VoidCallback onRemove; + + const ProductBlock({ + super.key, + required this.product, + required this.onUpdate, + required this.onRemove, + }); + + @override + State createState() => _ProductBlockState(); +} + +class _ProductBlockState extends State { + 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!.capitalize()}', + 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)), + ], + ), + ); + } +} + +class ProductModel { + final String sku; + final String productName; + final int? sale; + final int? inventory; + final String? comments; + final String? date; + + ProductModel({ + required this.sku, + required this.productName, + this.sale, + this.inventory, + this.comments, + this.date, + }); +} diff --git a/lib/screens/assign_task_dash_board_screen.dart b/lib/screens/assign_task_dash_board_screen.dart index 0556208..990e696 100644 --- a/lib/screens/assign_task_dash_board_screen.dart +++ b/lib/screens/assign_task_dash_board_screen.dart @@ -119,15 +119,15 @@ class _AssignTaskDashBoardScreenState extends State { ), ), ), - const SizedBox(height: 15), - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'MANAGE SCs', - onPressed: () {}, - ), + // const SizedBox(height: 15), + // CommonElevatedButton( + // backgroundColor: const Color(0xff004791), + // borderRadius: 30, + // width: double.infinity, + // height: kToolbarHeight - 10, + // text: 'MANAGE SCs', + // onPressed: () {}, + // ), ], ), ), diff --git a/lib/screens/assign_tasks_screen.dart b/lib/screens/assign_tasks_screen.dart index 628c773..17eb00e 100644 --- a/lib/screens/assign_tasks_screen.dart +++ b/lib/screens/assign_tasks_screen.dart @@ -1,6 +1,7 @@ import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/provider/task_provider.dart'; import 'package:cheminova/screens/confirm_task_screen.dart'; +import 'package:cheminova/utils/string_extension.dart'; import 'package:provider/provider.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_background.dart'; @@ -149,15 +150,6 @@ class _AssignTasksScreenState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Assign Tasks', - style: TextStyle( - fontSize: 24, - color: Colors.white, - fontWeight: FontWeight.bold, - fontFamily: 'Anek', - ), - ), const SizedBox(height: 20), SearchField( suggestionsDecoration: SuggestionDecoration( @@ -166,7 +158,7 @@ class _AssignTasksScreenState extends State { bottomRight: Radius.circular(8), ), border: Border.all( - color: Colors.grey.withOpacity(0.5), + color: Colors.white.withOpacity(0.5), ), ), suggestionItemDecoration: BoxDecoration( @@ -216,7 +208,7 @@ class _AssignTasksScreenState extends State { (e) => SearchFieldListItem( e.name!, item: e, - child: Text(e.name!), + child: Text(e.name!.capitalize()), ), ) .toList(), @@ -266,7 +258,7 @@ class _AssignTasksScreenState extends State { taskProvider.setSelectedTask(value); }, title: const Text( - "Update Sales Data Data", + "Update Sales Data", ), ), ), @@ -321,6 +313,7 @@ class _AssignTasksScreenState extends State { ), child: TextFormField( controller: taskProvider.noteController, + textCapitalization: TextCapitalization.sentences, expands: true, maxLines: null, minLines: null, @@ -345,11 +338,12 @@ class _AssignTasksScreenState extends State { if (taskProvider.selectedTask == 'Update Inventory Data' || taskProvider.selectedTask == 'Visit RD/PD' || - taskProvider.selectedTask == 'Update Sales Data') ...{ + taskProvider.selectedTask == + 'Update Sales Data') ...{ Padding( padding: const EdgeInsets.symmetric( - horizontal: 15.0, - vertical: 5, + horizontal: 5.0, + vertical: 15, ), child: DropdownButtonFormField( decoration: const InputDecoration( @@ -384,7 +378,7 @@ class _AssignTasksScreenState extends State { if (selectedDistributorType != null) Padding( padding: const EdgeInsets.symmetric( - horizontal: 15.0, vertical: 25), + horizontal: 5.0, vertical: 15), child: DropdownButtonFormField( decoration: const InputDecoration( labelText: 'Select Distributor Name', @@ -400,7 +394,7 @@ class _AssignTasksScreenState extends State { .map((PdRdResponseModel distributor) { return DropdownMenuItem( value: distributor, - child: Text(distributor.name!), + child: Text(distributor.name!.capitalize()), ); }).toList(), onChanged: (value) { diff --git a/lib/screens/daily_tasks_screen.dart b/lib/screens/daily_tasks_screen.dart index 99efd8c..b2218de 100644 --- a/lib/screens/daily_tasks_screen.dart +++ b/lib/screens/daily_tasks_screen.dart @@ -1,5 +1,5 @@ import 'package:cheminova/screens/collect_kyc_screen.dart'; -import 'package:cheminova/screens/update_inventory_screen.dart'; +import 'package:cheminova/screens/select_distributer_screen.dart'; import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:flutter/material.dart'; import 'package:cheminova/screens/visit_Dealers_screen.dart'; @@ -61,18 +61,41 @@ class DailyTasksScreen extends StatelessWidget { 'Update Sales Data', '', onTap: () { - Navigator.push(context,MaterialPageRoute(builder: (context) => const DisplaySalesScreen(),)); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const DisplaySalesScreen(), + )); }, ), const SizedBox(height: 5), - _buildCustomCard('Update Inventory Data', '',onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const UpdateInventoryScreen(),)); - },), + _buildCustomCard( + 'Update Inventory Data', + '', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const SelectDistributerScreen( + task: "Add Products", + ), + )); + }, + ), const SizedBox(height: 5), - _buildCustomCard('Collect KYC Documents', '',onTap:() { - Navigator.push(context, MaterialPageRoute( - builder: (context) => const CollectKycScreen(),)); - }, ), + _buildCustomCard( + 'Collect KYC Documents', + '', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const CollectKycScreen(), + )); + }, + ), ], ), ), @@ -84,7 +107,8 @@ class DailyTasksScreen extends StatelessWidget { ); } - Widget _buildCustomCard(String title, String subtitle, {void Function()? onTap}) { + Widget _buildCustomCard(String title, String subtitle, + {void Function()? onTap}) { return Container( margin: const EdgeInsets.only(bottom: 10), decoration: BoxDecoration( @@ -103,9 +127,9 @@ class DailyTasksScreen extends StatelessWidget { ), subtitle: subtitle.isNotEmpty ? Text( - subtitle, - style: const TextStyle(color: Colors.white70, fontSize: 13), - ) + subtitle, + style: const TextStyle(color: Colors.white70, fontSize: 13), + ) : null, onTap: onTap, ), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 980fb4e..ad4e7f8 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -8,8 +8,7 @@ import 'package:cheminova/screens/mark_attendence_screen.dart'; import 'package:cheminova/screens/notification_screen.dart'; import 'package:cheminova/screens/products_manual_screen.dart'; import 'package:cheminova/screens/rejected_application_screen.dart'; -import 'package:cheminova/screens/product_sales_data.dart'; -import 'package:cheminova/screens/update_inventory_screen.dart'; +import 'package:cheminova/screens/select_distributer_screen.dart'; import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:cheminova/widgets/common_drawer.dart'; import 'package:flutter/material.dart'; @@ -119,13 +118,13 @@ class _HomePageState extends State { Row( children: [ Expanded( - child: _buildCustomCard('Display\nSales data', - 'Quickly display Sales\n', onTap: () { + child: _buildCustomCard('Collect \nKYC Data', + 'Scan and upload KYC Documents', onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => - const DisplaySalesScreen(), + const CollectKycScreen(), )); }), ), @@ -139,7 +138,9 @@ class _HomePageState extends State { context, MaterialPageRoute( builder: (context) => - const UpdateInventoryScreen(), + const SelectDistributerScreen( + task: "Update Inventory", + ), )); }), ), @@ -152,8 +153,8 @@ class _HomePageState extends State { children: [ Expanded( child: _buildCustomCard( - 'Visit RD/PD', - '\n\n', + 'Visit RD/PD\n\n', + '', onTap: () { Navigator.push( context, @@ -169,33 +170,17 @@ class _HomePageState extends State { width: 12, ), Expanded( - child: _buildCustomCard( - 'Product\nSales Data Visibility', '', + child: _buildCustomCard('Update\nSales Data\n', '', onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const ProductSalesData(), - )); - }), - ), - ], - ), - const SizedBox( - height: 5, - ), - Row( - children: [ - Expanded( - child: _buildCustomCard('Collect \nKYC Data', - 'Scan and upload KYC Documents', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const CollectKycScreen(), - )); + context, + MaterialPageRoute( + builder: (context) => + const SelectDistributerScreen( + task: "Update Sales", + ), + ), + ); }), ), ], @@ -271,10 +256,6 @@ class _HomePageState extends State { ), ), const SizedBox(height: 12), - TextButton( - onPressed: () => throw Exception(), - child: const Text("Throw Test Exception"), - ), ], ), ), diff --git a/lib/screens/rejected_application_screen.dart b/lib/screens/rejected_application_screen.dart index bc771c5..5b38045 100644 --- a/lib/screens/rejected_application_screen.dart +++ b/lib/screens/rejected_application_screen.dart @@ -94,81 +94,83 @@ class MyListView extends StatelessWidget { RejectedApplicationResponse item = value.rejectedApplicationList[index]; return Padding( padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), - child: ExpansionTile( - collapsedBackgroundColor: Colors.white, - backgroundColor: Colors.white, - title: Text( - item.tradeName ?? '', - style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text((DateFormat("dd MMMM yyyy") - .format(DateTime.parse(item.createdAt ?? '')))), - Text(item.sId ?? ''), - for (var note in item.notes!) Text(note.message ?? ''), + child: ClipRRect( + borderRadius: BorderRadius.circular(15), // Rounded corner + child: ExpansionTile( + collapsedBackgroundColor: Colors.white, + backgroundColor: Colors.white, + title: Text( + item.tradeName ?? '', + style: + const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text((DateFormat("dd MMMM yyyy") + .format(DateTime.parse(item.createdAt ?? '')))), + Text("ID:${item.sId ?? ''}"), + for (var note in item.notes!) Text(note.message ?? ''), + ], + ), + children: [ + ListTile( + title: Text('Address: ${item.address ?? ''}'), + ), + ListTile( + title: Text('City: ${item.city ?? ''}'), + ), + ListTile( + title: Text('State: ${item.state ?? ''}'), + ), + ListTile( + title: Text('Pincode: ${item.pincode ?? ''}'), + ), + ListTile( + title: Text('Mobile: ${item.mobileNumber ?? ''}'), + ), + ListTile( + title: Text('Status: ${item.status ?? ''}'), + ), + ListTile( + title: Text( + 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), + ), + ListTile( + title: Text('PAN Number: ${item.panNumber ?? ''}'), + ), + Image.network(item.panImg!.url ?? '', height: 250, width: 250), + ListTile( + title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'), + ), + Image.network(item.aadharImg?.url ?? '', + height: 250, width: 250), + ListTile( + title: Text('GST Number: ${item.gstNumber ?? ''}'), + ), + Image.network(item.gstImg!.url ?? '', height: 250, width: 250), + const ListTile( + title: Text('Pesticide License: '), + ), + if (item.pesticideLicenseImg != null) + Image.network(item.pesticideLicenseImg!.url ?? '', + height: 250, width: 250), + const ListTile( + title: Text('selfieEntranceImg: '), + ), + Image.network(item.selfieEntranceImg!.url ?? '', + height: 250, width: 250), + const ListTile( + title: Text('Notes:'), + ), + if (item.notes != null) + for (var note in item.notes!) + ListTile( + contentPadding: const EdgeInsets.only(left: 20), + title: Text(note.message ?? ''), + ), ], ), - children: [ - ListTile( - title: Text('Address: ${item.address ?? ''}'), - ), - ListTile( - title: Text('City: ${item.city ?? ''}'), - ), - ListTile( - title: Text('State: ${item.state ?? ''}'), - ), - ListTile( - title: Text('Pincode: ${item.pincode ?? ''}'), - ), - ListTile( - title: Text('Mobile: ${item.mobileNumber ?? ''}'), - ), - ListTile( - title: Text('Status: ${item.status ?? ''}'), - ), - ListTile( - title: Text( - 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), - ), - ListTile( - title: Text('PAN Number: ${item.panNumber ?? ''}'), - ), - Image.network(item.panImg!.url ?? '', height: 250, width: 250), - ListTile( - title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'), - ), - Image.network(item.aadharImg?.url ?? '', height: 250, width: 250), - ListTile( - title: Text('GST Number: ${item.gstNumber ?? ''}'), - ), - Image.network(item.gstImg!.url ?? '', height: 250, width: 250), - const ListTile( - title: Text('Pesticide License: '), - ), - - if (item.pesticideLicenseImg != null) - Image.network(item.pesticideLicenseImg!.url ?? '', - height: 250, width: 250), - // if (item['fertilizer_license_img'] != null) - // Image.network(item['fertilizer_license_img']['url'] ?? ''), - const ListTile( - title: Text('selfieEntranceImg: '), - ), - Image.network(item.selfieEntranceImg!.url ?? '', - height: 250, width: 250), - const ListTile( - title: Text('Notes:'), - ), - if (item.notes != null) - for (var note in item.notes!) - ListTile( - contentPadding: const EdgeInsets.only(left: 20), - title: Text(note.message ?? ''), - ), - ], ), ); }, diff --git a/lib/screens/retailer_details_screen.dart b/lib/screens/retailer_details_screen.dart index 9763b68..e55b1e7 100644 --- a/lib/screens/retailer_details_screen.dart +++ b/lib/screens/retailer_details_screen.dart @@ -45,6 +45,7 @@ class RetailerDetailsScreenState extends State { children: [ CommonTextFormField( title: 'Trade Name', + textCapitalization: TextCapitalization.words, fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { @@ -56,6 +57,7 @@ class RetailerDetailsScreenState extends State { const SizedBox(height: 15), CommonTextFormField( title: 'Name', + textCapitalization: TextCapitalization.words, fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { @@ -65,6 +67,19 @@ class RetailerDetailsScreenState extends State { }, controller: value.nameController), const SizedBox(height: 15), + CommonTextFormField( + title: 'Email', + textCapitalization: TextCapitalization.none, + keyboardType: TextInputType.emailAddress, + fillColor: Colors.white, + validator: (String? value) { + if (value!.isEmpty) { + return 'Email cannot be empty'; + } + return null; + }, + controller: value.emailController), + const SizedBox(height: 15), CommonTextFormField( title: 'Address', fillColor: Colors.white, @@ -110,6 +125,7 @@ class RetailerDetailsScreenState extends State { CommonTextFormField( maxLength: 6, title: 'Pincode', + keyboardType: TextInputType.number, fillColor: Colors.white, inputFormatters: [ FilteringTextInputFormatter.digitsOnly @@ -125,6 +141,7 @@ class RetailerDetailsScreenState extends State { CommonTextFormField( maxLength: 10, title: 'Mobile Number', + keyboardType: TextInputType.number, fillColor: Colors.white, inputFormatters: [ FilteringTextInputFormatter.digitsOnly @@ -140,6 +157,7 @@ class RetailerDetailsScreenState extends State { CommonTextFormField( maxLength: 12, title: 'Aadhar Number', + keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly ], diff --git a/lib/screens/update_inventory_screen.dart b/lib/screens/select_distributer_screen.dart similarity index 77% rename from lib/screens/update_inventory_screen.dart rename to lib/screens/select_distributer_screen.dart index 81edc7b..720324a 100644 --- a/lib/screens/update_inventory_screen.dart +++ b/lib/screens/select_distributer_screen.dart @@ -1,6 +1,8 @@ import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/provider/pd_rd_provider.dart'; import 'package:cheminova/screens/add_products_screen.dart'; +import 'package:cheminova/screens/add_sales_products_screen.dart'; +import 'package:cheminova/utils/string_extension.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_drawer.dart'; @@ -8,14 +10,16 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class UpdateInventoryScreen extends StatefulWidget { - const UpdateInventoryScreen({super.key}); +class SelectDistributerScreen extends StatefulWidget { + final String task; + const SelectDistributerScreen({super.key, required this.task}); @override - State createState() => _UpdateInventoryScreenState(); + State createState() => + _SelectDistributerScreenState(); } -class _UpdateInventoryScreenState extends State { +class _SelectDistributerScreenState extends State { String? selectedDistributorType; PdRdResponseModel? selectedDistributor; @@ -45,7 +49,7 @@ class _UpdateInventoryScreenState extends State { ), ], title: const Text( - 'Update Inventory Data', + 'Select Distributor', style: TextStyle( fontSize: 20, color: Colors.black, @@ -69,18 +73,35 @@ class _UpdateInventoryScreenState extends State { selectedDistributorType == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text( - 'Please select distributor type and distributor')), + content: + Text('Please select distributor type and distributor'), + ), ); } else { Navigator.push( context, - MaterialPageRoute( - builder: (context) => AddProductsScreen( - distributor: selectedDistributor!, - distributorType: selectedDistributorType!, - ), - ), + MaterialPageRoute(builder: (context) { + if (widget.task == 'Update Inventory') { + return AddProductsScreen( + distributor: selectedDistributor!, + distributorType: selectedDistributorType!, + ); + } else if (widget.task == 'Update Sales') { + return AddSalesProductScreen( + distributorType: + selectedDistributorType == 'Principal Distributor' + ? 'PrincipalDistributor' + : 'RetailDistributor', + tradeName: selectedDistributorType == + 'Principal Distributor' + ? selectedDistributor!.shippingAddress!.tradeName + : selectedDistributor!.kyc!.tradeName, + pdRdId: selectedDistributor!.id!, + ); + } else { + return Container(); + } + }), ); } }, @@ -143,7 +164,7 @@ class _UpdateInventoryScreenState extends State { .map((PdRdResponseModel distributor) { return DropdownMenuItem( value: distributor, - child: Text(distributor.name!), + child: Text(distributor.name!.capitalize()), ); }).toList(), onChanged: (value) { diff --git a/lib/screens/task_management_screen.dart b/lib/screens/task_management_screen.dart index 71a223e..ab1a784 100644 --- a/lib/screens/task_management_screen.dart +++ b/lib/screens/task_management_screen.dart @@ -198,7 +198,7 @@ class _TaskManagementScreenState extends State { padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.6), + color: const Color(0xffB4D1E5).withOpacity(0.8), borderRadius: BorderRadius.circular(16.0), ), child: child, @@ -220,21 +220,38 @@ class _TaskManagementScreenState extends State { fontWeight: FontWeight.bold, ), ), - const SizedBox(height: 10), + const SizedBox(height: 5), Text( "Task: ${task.task}", style: const TextStyle( fontFamily: 'Anek', ), ), - const SizedBox(height: 10), + const SizedBox(height: 5), + if (task.addedFor != null) ...{ + task.addedFor == 'PrincipalDistributor' + ? Text( + "PD: ${task.tradename}", + ) + : Text("RD: ${task.tradename}"), + const SizedBox(height: 5), + }, Text( 'Status: ${task.taskStatus}', style: const TextStyle( fontFamily: 'Anek', ), ), - const SizedBox(height: 10), + const SizedBox(height: 5), + if (task.note != null) ...{ + Text( + 'Note: ${task.note}', + style: const TextStyle( + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 5), + }, Text( 'Deadline:$formatedDate', style: const TextStyle( diff --git a/lib/screens/verification_documents_screen.dart b/lib/screens/verification_documents_screen.dart index c03ec65..8358513 100644 --- a/lib/screens/verification_documents_screen.dart +++ b/lib/screens/verification_documents_screen.dart @@ -28,6 +28,7 @@ class VerifyDocumentsScreen extends StatelessWidget { { 'Trade Name': value.tradeNameController.text, 'Name': value.nameController.text, + 'Email': value.emailController.text, 'Address': value.addressController.text, 'Town/City': value.city.text, 'District': value.districtController.text, diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index 9aaa933..a1a30cc 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -1,5 +1,6 @@ class ApiUrls { - static const String baseUrl = 'https://cheminova-api-2.onrender.com/api/'; + // static const String baseUrl = 'https://cheminova-api-2.onrender.com/api/'; + static const String baseUrl = 'https://api.cnapp.co.in/api/'; static const String loginUrl = 'territorymanager/login'; static const String profileUrl = 'territorymanager/my-profile'; static const String markAttendanceUrl = 'v1/markattendance/territorymanager'; @@ -19,5 +20,7 @@ class ApiUrls { static const String assignTask = 'task/assign-task'; static const String getProductsManual = 'productmanual/getall'; static const String getAllTasks = 'task/alltasks/'; + static const String salesTaskUrl = '${baseUrl}product/getAll/user/'; + static const String postSalesTaskUrl = '${baseUrl}sales/add-TM'; static const String updateTaskInventoryUrl = ''; } diff --git a/lib/utils/string_extension.dart b/lib/utils/string_extension.dart new file mode 100644 index 0000000..5d92948 --- /dev/null +++ b/lib/utils/string_extension.dart @@ -0,0 +1,5 @@ +extension StringExtension on String { + String capitalize() { + return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; + } +} diff --git a/lib/widgets/common_text_form_field.dart b/lib/widgets/common_text_form_field.dart index 284ef30..1fad71b 100644 --- a/lib/widgets/common_text_form_field.dart +++ b/lib/widgets/common_text_form_field.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; class CommonTextFormField extends StatelessWidget { final String title; final TextEditingController? controller; + final TextCapitalization textCapitalization; final String? Function(String?)? validator; final Color? fillColor; final bool? readOnly; @@ -28,6 +29,7 @@ class CommonTextFormField extends StatelessWidget { this.inputFormatters, this.maxLength, this.onChanged, + this.textCapitalization = TextCapitalization.sentences, this.obscureText = false, }); @@ -44,6 +46,7 @@ class CommonTextFormField extends StatelessWidget { // fontFamily: 'Anek')), TextFormField( controller: controller, + textCapitalization: textCapitalization, readOnly: readOnly ?? false, maxLines: maxLines, maxLength: maxLength,