From 86a9b751ba4fb24a9a389a789f94d3254cf701be Mon Sep 17 00:00:00 2001 From: Vaibhav Date: Sun, 29 Sep 2024 22:09:36 +0530 Subject: [PATCH] Submit VisitUrl Added --- lib/provider/add_sales_product_provider.dart | 1 - lib/provider/collect_kyc_provider.dart | 8 + lib/provider/pd_rd_provider.dart | 52 ++-- lib/provider/products_provider.dart | 87 +++--- lib/provider/rejected_provider.dart | 31 +- lib/provider/select_task_provider.dart | 36 ++- lib/screens/Add_products_screen.dart | 56 +++- lib/screens/Attendance_success.dart | 25 +- lib/screens/Update_inventorytask_screen.dart | 89 +++--- lib/screens/add_sales_product_screen.dart | 29 +- lib/screens/calendar_screen.dart | 114 +++---- lib/screens/change_password_screen.dart | 155 +++++----- lib/screens/collect_kyc_screen.dart | 155 +++++----- lib/screens/daily_tasks_screen.dart | 143 +++++---- lib/screens/data_submit_successfull.dart | 36 ++- lib/screens/forgot_password_screen.dart | 109 ++++--- lib/screens/home_screen.dart | 77 ++--- lib/screens/login_screen.dart | 286 +++++++++--------- lib/screens/mark_attendence_screen.dart | 117 ++++--- lib/screens/notification_screen.dart | 20 +- lib/screens/on_leave_screen.dart | 281 +++++++++-------- lib/screens/password_change_screen.dart | 4 +- lib/screens/product_purchase_data.dart | 47 +-- lib/screens/products_manual_screen.dart | 18 +- lib/screens/profile_screen.dart | 67 ++-- lib/screens/rejected_application_screen.dart | 69 ++--- lib/screens/retailer_details_screen.dart | 66 ++-- lib/screens/sales_task_screen.dart | 42 +-- lib/screens/select_taskkyc_screen.dart | 52 ++-- lib/screens/splash_screen.dart | 106 ++++--- lib/screens/summary_screen.dart | 94 +++--- lib/screens/update_inventory_screen.dart | 227 +++++++------- lib/screens/upload_documents_screen.dart | 49 +-- .../verification_documents_screen.dart | 125 ++++---- lib/screens/verify_code_screen.dart | 5 + lib/screens/verify_phone_screen.dart | 80 ++--- lib/screens/verify_successfull_screen.dart | 42 ++- lib/screens/view_pdf_screen.dart | 29 +- lib/screens/visit_rd_pd_screen.dart | 157 +++++----- lib/services/api_client.dart | 43 ++- 40 files changed, 1778 insertions(+), 1451 deletions(-) diff --git a/lib/provider/add_sales_product_provider.dart b/lib/provider/add_sales_product_provider.dart index c56cdcd..2da3e0e 100644 --- a/lib/provider/add_sales_product_provider.dart +++ b/lib/provider/add_sales_product_provider.dart @@ -1,5 +1,4 @@ import 'dart:convert'; - import 'package:cheminova/constants/constant.dart'; import 'package:cheminova/models/SalesTaskResponse.dart'; import 'package:cheminova/screens/data_submit_successfull.dart'; diff --git a/lib/provider/collect_kyc_provider.dart b/lib/provider/collect_kyc_provider.dart index aae2c60..c3fb89e 100644 --- a/lib/provider/collect_kyc_provider.dart +++ b/lib/provider/collect_kyc_provider.dart @@ -22,6 +22,7 @@ class CollectKycProvider extends ChangeNotifier { late TabController tabController; final _apiClient = ApiClient(); + // Text controllers for form fields final selectTaskController= TextEditingController(); final tradeNameController = TextEditingController(); final nameController = TextEditingController(); @@ -38,6 +39,7 @@ class CollectKycProvider extends ChangeNotifier { String? selectedDistributor; final retailerDetailsFormKey = GlobalKey(); + // Files to hold picked images for various documents File? panCard; File? aadharCard; @@ -47,6 +49,7 @@ class CollectKycProvider extends ChangeNotifier { File? selfieEntranceBoard; final ImagePicker _picker = ImagePicker(); + // Method to pick an image from the gallery or camera Future pickImage(ImageSource source, String documentType) async { final pickedFile = await _picker.pickImage(source: source); @@ -75,6 +78,7 @@ class CollectKycProvider extends ChangeNotifier { } notifyListeners(); } + // Method to show image picker options (gallery/camera) in a modal void showPicker(BuildContext context, String documentType) { showModalBottomSheet( @@ -145,6 +149,7 @@ class CollectKycProvider extends ChangeNotifier { setLoading(false); } } + // Method to validate form fields before submission void validateFields(BuildContext context) { if (tradeNameController.text.trim().isEmpty) { @@ -225,6 +230,7 @@ class CollectKycProvider extends ChangeNotifier { submitCollectKycForm(context); } } + // Method to submit the KYC form to the API Future submitCollectKycForm(BuildContext context) async { final dio = Dio(); @@ -232,6 +238,8 @@ class CollectKycProvider extends ChangeNotifier { final headers = {'Authorization': 'Bearer $token'}; // Construct the FormData + // Create multipart form data + final data = FormData.fromMap({ 'name': nameController.text.trim(), 'trade_name': tradeNameController.text.trim(), diff --git a/lib/provider/pd_rd_provider.dart b/lib/provider/pd_rd_provider.dart index 1b00e65..f8c1e43 100644 --- a/lib/provider/pd_rd_provider.dart +++ b/lib/provider/pd_rd_provider.dart @@ -6,60 +6,78 @@ import 'package:cheminova/services/api_urls.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +// Provider class to manage the Principal Distributor (PD) and Retailer Distributor (RD) data class PdRdProvider extends ChangeNotifier { - String? selectedDistributorType; + String? + selectedDistributorType; // Holds the selected distributor type (PD or RD) - bool _isLoading = false; + bool _isLoading = + false; // Flag to indicate whether data is currently being loaded - bool get isLoading => _isLoading; + bool get isLoading => _isLoading; // Getter for loading state - List _pdRdList = []; + List _pdRdList = []; // List to store fetched PD/RD data - List get pdRdList => _pdRdList; + List get pdRdList => + _pdRdList; // Getter to access the PD/RD list - GetPdRdResponse? selectedPdRd; + GetPdRdResponse? selectedPdRd; // Holds the currently selected PD/RD - final _apiClient = ApiClient(); + final _apiClient = + ApiClient(); // Instance of the API client to make network requests + // Method to update the loading state and notify listeners void setLoading(bool loading) { _isLoading = loading; - notifyListeners(); + notifyListeners(); // Notify listeners to update the UI } + // Method to fetch PD/RD data from the API based on the selected distributor type Future getPdRd() async { - setLoading(true); + setLoading(true); // Show loading indicator while data is being fetched try { + // Make GET request to fetch PD/RD data, replacing spaces in the selected distributor type Response response = await _apiClient.get( ApiUrls.getPdRdUrl + selectedDistributorType!.replaceAll(' ', '')); - setLoading(false); + + setLoading(false); // Hide loading indicator after response + if (response.statusCode == 200) { + // If response is successful, parse the data into a list of GetPdRdResponse objects _pdRdList = (response.data as List) .map((json) => GetPdRdResponse.fromJson(json)) .toList(); - notifyListeners(); + notifyListeners(); // Notify listeners to refresh the UI with new data } else { + // Log an error message if the response is not successful debugPrint("Failed to load data: ${response.statusCode}"); } } catch (e) { + // Log any exceptions that occur during the API call debugPrint("Error occurred: $e"); } finally { + // Always hide the loading indicator once the operation completes setLoading(false); } } + // Method to update the selected distributor type and fetch corresponding PD/RD data void updateDistributorType(String? val) { - selectedDistributorType = val; - selectedPdRd = null; - notifyListeners(); - getPdRd(); + selectedDistributorType = val; // Update selected distributor type + selectedPdRd = null; // Reset the selected PD/RD + notifyListeners(); // Notify listeners to update the UI + getPdRd(); // Fetch the PD/RD data for the selected distributor type } + // Method to update the selected PD/RD and navigate to the Add Products screen updatePdRdValue(GetPdRdResponse? val) { - selectedPdRd = val; - notifyListeners(); + selectedPdRd = val; // Update selected PD/RD + notifyListeners(); // Notify listeners to update the UI + // Delay navigation slightly to ensure smooth transition Future.delayed(const Duration(milliseconds: 500), () { if (selectedPdRd != null && selectedDistributorType != null) { + // Navigate to the AddProductsScreen, passing selected distributor type and PD/RD details Navigator.push( navigatorKey.currentContext!, MaterialPageRoute( diff --git a/lib/provider/products_provider.dart b/lib/provider/products_provider.dart index eba1dba..9e0098f 100644 --- a/lib/provider/products_provider.dart +++ b/lib/provider/products_provider.dart @@ -7,23 +7,25 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import '../models/products_response.dart'; +// Provider class responsible for handling product data and API requests class ProductProvider extends ChangeNotifier { - final _apiClient = ApiClient(); - ProductResponse? productResponse; - List productList = []; - List searchList = []; + final _apiClient = ApiClient(); // API client for making HTTP requests + ProductResponse? productResponse; // Response object for product list + List productList = []; // List of products + List searchList = []; // Filtered list of products for search - bool _isLoading = false; + bool _isLoading = false; // Flag for loading state + bool get isLoading => _isLoading; // Getter for loading state - bool get isLoading => _isLoading; - - List selectedProducts = []; + List selectedProducts = []; // List of selected products + // Set loading state and notify listeners void setLoading(bool loading) { _isLoading = loading; - notifyListeners(); + notifyListeners(); // Notify listeners to rebuild UI } + // Filter products based on the search query (either by product name or SKU) void filterProducts(String query) { searchList = productList.where((product) { final productNameLower = product.productName.toLowerCase(); @@ -32,27 +34,29 @@ class ProductProvider extends ChangeNotifier { return productNameLower.contains(searchLower) || productSkuLower.contains(searchLower); }).toList(); - notifyListeners(); + notifyListeners(); // Notify listeners to update UI with search results } + // Fetch the list of products from the API Future getProducts() async { - Response response = await _apiClient.get(ApiUrls.getProducts); - debugPrint('Response: $response'); - setLoading(false); - if (response.statusCode == 200) { - productResponse = ProductResponse.fromJson(response.data); - productList = productResponse!.products! - .map((product) => - ProductModel(sku: product.sKU!, productName: product.name!)) - .toList(); - notifyListeners(); - } + Response response = await _apiClient.get(ApiUrls.getProducts); + debugPrint('Response: $response'); + setLoading(false); + if (response.statusCode == 200) { + productResponse = ProductResponse.fromJson(response.data); + productList = productResponse!.products! + .map((product) => ProductModel(sku: product.sKU!, productName: product.name!)) + .toList(); + notifyListeners(); // Notify listeners to update the UI with the product list + } } + // Submit selected products to the API Future submitProducts( {required String distributorType, required String pdRdId, String? inventoryId}) async { setLoading(true); try { + // Send POST request to submit products Response response = await _apiClient.post(ApiUrls.submitProductUrl, data: json.encode({ "addedFor": distributorType.replaceAll(' ', ''), @@ -60,23 +64,26 @@ class ProductProvider extends ChangeNotifier { "products": selectedProducts.map((e) => e.toJson()).toList() })); setLoading(false); + + // Handle successful response 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) { + + // Update task if inventoryId is provided + if (inventoryId != null) { + _apiClient.put(ApiUrls.updateTaskInventoryUrl + inventoryId, data: null).then((value) { debugPrint('Task Updated'); if (value.statusCode == 200) { - resetProducts(); + resetProducts(); // Reset selected products and product list Navigator.push( navigatorKey.currentContext!, MaterialPageRoute( builder: (context) => const DataSubmitSuccessFullScreen())); - } - else{ + } else { ScaffoldMessenger.of( navigatorKey.currentContext!, ).showSnackBar( @@ -84,8 +91,7 @@ class ProductProvider extends ChangeNotifier { ); } }); - } - else{ + } else { resetProducts(); Navigator.push( navigatorKey.currentContext!, @@ -95,30 +101,34 @@ class ProductProvider extends ChangeNotifier { } } catch (e) { setLoading(false); - debugPrint("Error: $e"); + debugPrint("Error: $e"); // Handle any errors during the request } } + // Reset selected products and product list void resetProducts() { selectedProducts.clear(); productList.clear(); productResponse = null; - notifyListeners(); + notifyListeners(); // Notify listeners to update the UI } } +// Model class for Product class ProductModel { - String sku; - String productName; - int? sale; - int? inventory; + String sku; // SKU of the product + String productName; // Name of the product + int? sale; // Sale quantity (optional) + int? inventory; // Inventory quantity (optional) + // Constructor for ProductModel ProductModel( {required this.sku, - required this.productName, - this.sale, - this.inventory}); + required this.productName, + this.sale, + this.inventory}); + // Factory method to create a ProductModel from JSON factory ProductModel.fromJson(Map json) { return ProductModel( sku: json['SKU'], @@ -127,8 +137,9 @@ class ProductModel { inventory: json['Inventory']); } - get comments => null; + get comments => null; // Placeholder for any future comments functionality + // Convert ProductModel to JSON format for API requests Map toJson() { return { 'SKU': sku, diff --git a/lib/provider/rejected_provider.dart b/lib/provider/rejected_provider.dart index a23f828..9f2b7ec 100644 --- a/lib/provider/rejected_provider.dart +++ b/lib/provider/rejected_provider.dart @@ -5,35 +5,44 @@ import 'package:flutter/material.dart'; import '../services/api_client.dart'; import '../services/api_urls.dart'; +// Provider class responsible for managing rejected applications data and API calls class RejectedProvider extends ChangeNotifier { + // Constructor to automatically fetch rejected applications when the provider is created RejectedProvider() { - getRejectedApplication(); + getRejectedApplication(); // Fetch rejected applications } - final _apiClient = ApiClient(); - RejectedApplicationResponse? rejectedApplicationResponse; - List rejectedApplicationList = []; - bool _isLoading = false; - bool get isLoading => _isLoading; + final _apiClient = ApiClient(); // API client instance for making HTTP requests + RejectedApplicationResponse? rejectedApplicationResponse; // Response object for rejected applications + List rejectedApplicationList = []; // List to store rejected applications + bool _isLoading = false; // Flag to track loading state + bool get isLoading => _isLoading; // Getter for the loading state + + // Function to update loading state and notify listeners to rebuild UI void setLoading(bool loading) { _isLoading = loading; - notifyListeners(); + notifyListeners(); // Notify listeners that the loading state has changed } + // Fetch rejected applications from the API Future getRejectedApplication() async { - setLoading(true); + setLoading(true); // Set loading state to true while fetching data try { + // Send GET request to the API to fetch rejected applications Response response = await _apiClient.get(ApiUrls.rejectedApplication); - setLoading(false); + setLoading(false); // Set loading state to false after getting a response + + // If the response is successful (status code 200), parse the data if (response.statusCode == 200) { + // Map each item in the response data to a RejectedApplicationResponse object rejectedApplicationList = (response.data as List) .map((e) => RejectedApplicationResponse.fromJson(e)) .toList(); - notifyListeners(); + notifyListeners(); // Notify listeners to update the UI with the new data } } catch (e) { - setLoading(false); + setLoading(false); // In case of error, set loading state to false } } } diff --git a/lib/provider/select_task_provider.dart b/lib/provider/select_task_provider.dart index 26a6320..8f7e05c 100644 --- a/lib/provider/select_task_provider.dart +++ b/lib/provider/select_task_provider.dart @@ -6,40 +6,50 @@ import 'package:flutter/material.dart'; import '../services/api_client.dart'; import '../services/api_urls.dart'; +// Provider class responsible for managing tasks and fetching task data from the API class SelectTaskProvider extends ChangeNotifier { + + // Constructor that automatically fetches tasks when the provider is initialized SelectTaskProvider() { - getTask(); + getTask(); // Fetch tasks upon provider creation } - final _apiClient = ApiClient(); - List tasksList = []; + final _apiClient = ApiClient(); // API client instance for handling HTTP requests + List tasksList = []; // List to store tasks fetched from the API - bool _isLoading = false; + bool _isLoading = false; // Flag to track the loading state (whether data is being loaded) - bool get isLoading => _isLoading; + bool get isLoading => _isLoading; // Getter for the loading state + // Function to set loading state and notify listeners to update UI void setLoading(bool loading) { _isLoading = loading; - notifyListeners(); + notifyListeners(); // Notifies listeners that the loading state has changed } + // Function to fetch tasks from the API Future getTask() async { - setLoading(true); + setLoading(true); // Set loading to true when data fetching starts try { + // Send GET request to the API to retrieve tasks Response response = await _apiClient.get(ApiUrls.kycSelectTaskUrl); - setLoading(false); + setLoading(false); // Set loading to false after data has been fetched + + // If the response is successful (status code 200), process the data if (response.statusCode == 200) { + // Parse the response data into a SelectTaskKycResponse object final data = SelectTaskKycResponse.fromJson(response.data); + + // Filter tasks to include only those that are not completed tasksList = data!.tasks! - .where( - (element) => element.taskStatus!.toLowerCase() != "completed") + .where((element) => element.taskStatus!.toLowerCase() != "completed") .toList(); - notifyListeners(); + notifyListeners(); // Notify listeners to update the UI with the new tasks } } catch (e) { - setLoading(false); + setLoading(false); // Set loading to false if an error occurs if (kDebugMode) { - print("Error occurred while fetching notifications: $e"); + print("Error occurred while fetching tasks: $e"); // Log the error in debug mode } } } diff --git a/lib/screens/Add_products_screen.dart b/lib/screens/Add_products_screen.dart index 162b092..8117e58 100644 --- a/lib/screens/Add_products_screen.dart +++ b/lib/screens/Add_products_screen.dart @@ -1,3 +1,4 @@ +// Import necessary packages and widgets for the application. import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_drawer.dart'; @@ -8,17 +9,20 @@ import 'package:provider/provider.dart'; import '../provider/products_provider.dart'; +// Stateful widget for the Add Products screen. class AddProductsScreen extends StatefulWidget { - final String distributorType; - final String tradeName; - final String pdRdId; - final String? inventoryId; + final String distributorType; // Type of distributor (e.g., Retailer, Wholesaler) + final String tradeName; // Trade name of the distributor + final String pdRdId; // Principal distributor/retailer ID + final String? inventoryId; // Optional inventory ID + // Constructor for AddProductsScreen, with required parameters. const AddProductsScreen({ super.key, required this.distributorType, required this.tradeName, - required this.pdRdId, this.inventoryId + required this.pdRdId, + this.inventoryId, }); @override @@ -26,11 +30,15 @@ class AddProductsScreen extends StatefulWidget { } class _AddProductsScreenState extends State { + // Controller for the search input field. final searchController = TextEditingController(); + + // ProductProvider for managing product-related logic. late ProductProvider productProvider; @override void initState() { + // Initialize the productProvider and fetch the products after the widget is built. productProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { productProvider.getProducts(); @@ -40,6 +48,7 @@ class _AddProductsScreenState extends State { @override void dispose() { + // Reset the products list when the screen is disposed. if (context.mounted) { productProvider.resetProducts(); } @@ -48,6 +57,7 @@ class _AddProductsScreenState extends State { @override Widget build(BuildContext context) { + // Build the main UI for the screen. return CommonBackground( child: Scaffold( backgroundColor: Colors.transparent, @@ -73,6 +83,8 @@ class _AddProductsScreenState extends State { elevation: 0 ), drawer: const CommonDrawer(), + + // BottomNavigationBar which holds the "Add Products" and "Submit" button. bottomNavigationBar: Consumer( builder: (context, value, child) => Column( mainAxisSize: MainAxisSize.min, @@ -86,6 +98,7 @@ class _AddProductsScreenState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ + // Floating Action Button to add products FloatingActionButton.extended( onPressed: () { showModalBottomSheet( @@ -95,11 +108,13 @@ class _AddProductsScreenState extends State { ), context: context, builder: (BuildContext context) { + // Display list of products with a search bar in the modal sheet. return Consumer( builder: (context, value, child) => StatefulBuilder( builder: (context, setState) { return Column( children: [ + // Search bar to filter products Padding( padding: const EdgeInsets.all(18.0), child: TextField( @@ -115,18 +130,21 @@ class _AddProductsScreenState extends State { } ) ), + // List of products (filtered or all) Expanded( child: ListView.builder( itemCount: searchController.text.isEmpty ? value.productList.length : value.searchList.length, itemBuilder: (context, index) { + // Check if the product is already selected. bool isAlreadySelected = value.selectedProducts.any( (selectedProduct) => selectedProduct.sku == value.productList[index].sku ); final data = searchController.text.isEmpty ? value.productList[index] : value.searchList[index]; + // Product tile with name, SKU, and selection functionality. return Card( child: ListTile( title: Text( @@ -160,6 +178,7 @@ class _AddProductsScreenState extends State { icon: const Icon(Icons.add, color: Colors.black), label: const Text('Add Products', style: TextStyle(color: Colors.black)) ), + // Display Submit button if products are selected. if (value.selectedProducts.isNotEmpty) ...[ const SizedBox(height: 16.0), Consumer( @@ -169,6 +188,7 @@ class _AddProductsScreenState extends State { height: kToolbarHeight - 10, text: 'SUBMIT', backgroundColor: const Color(0xff004791), + // Submit selected products, but first validate the data. onPressed: () { if (value.selectedProducts.isNotEmpty && value.selectedProducts.every((product) => @@ -181,6 +201,7 @@ class _AddProductsScreenState extends State { pdRdId: widget.pdRdId, inventoryId: widget.inventoryId ); } else { + // Show error message if data is incomplete. ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Please fill out all product details, including sale and inventory.') @@ -198,20 +219,24 @@ class _AddProductsScreenState extends State { ], ), ), + + // Main body of the screen that shows the selected products and the loader. body: Consumer( builder: (context, value, child) { return Stack( children: [ Column( children: [ + // Display the selected products in a list. if (value.selectedProducts.isNotEmpty) Expanded( child: ListView.builder( itemCount: value.selectedProducts.length, itemBuilder: (context, index) { + // Display individual product blocks. return ProductBlock( onUpdate: (updatedProduct) { - debugPrint('selected product le ${value.selectedProducts.length}'); + debugPrint('selected product length: ${value.selectedProducts.length}'); setState(() { value.selectedProducts[index] = updatedProduct; }); @@ -228,6 +253,7 @@ class _AddProductsScreenState extends State { ) ] ), + // Show loader when products are being fetched. (value.isLoading) ? Container( color: Colors.black12, @@ -243,10 +269,11 @@ class _AddProductsScreenState extends State { } } +// Widget to represent individual product blocks in the selected products list. class ProductBlock extends StatefulWidget { - final ProductModel product; - final ValueChanged onUpdate; - final VoidCallback onRemove; + final ProductModel product; // The product model to be displayed. + final ValueChanged onUpdate; // Callback when product data is updated. + final VoidCallback onRemove; // Callback to remove the product. const ProductBlock({ super.key, @@ -260,17 +287,20 @@ class ProductBlock extends StatefulWidget { } class _ProductBlockState extends State { + // Controllers for sale and inventory text fields. final saleController = TextEditingController(); final inventoryController = TextEditingController(); - String? errorMessage; + String? errorMessage; // Error message for input validation. @override void initState() { super.initState(); + // Initialize controllers with existing product data. saleController.text = (widget.product.sale ?? '').toString(); inventoryController.text = (widget.product.inventory ?? '').toString(); } + // Validate the inputs for sale and inventory fields. void validateInput() { setState(() { String? saleError; @@ -289,6 +319,7 @@ class _ProductBlockState extends State { int sale = int.parse(saleController.text); int inventory = int.parse(inventoryController.text); + // Update the product with validated data. widget.onUpdate(ProductModel( sku: widget.product.sku, productName: widget.product.productName, @@ -321,6 +352,7 @@ class _ProductBlockState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Text field for sale input. TextField( controller: saleController, onTapOutside: (event) => FocusScope.of(context).unfocus(), @@ -335,6 +367,7 @@ class _ProductBlockState extends State { enabled: true, onChanged: (_) => validateInput() ), + // Text field for inventory input. TextField( controller: inventoryController, onTapOutside: (event) => FocusScope.of(context).unfocus(), @@ -354,6 +387,7 @@ class _ProductBlockState extends State { ] ), ), + // Remove button for the product block. Positioned( top: 4, right: 4, @@ -366,4 +400,4 @@ class _ProductBlockState extends State { ) ); } -} \ No newline at end of file +} diff --git a/lib/screens/Attendance_success.dart b/lib/screens/Attendance_success.dart index ff2ac1b..5425ffb 100644 --- a/lib/screens/Attendance_success.dart +++ b/lib/screens/Attendance_success.dart @@ -1,7 +1,8 @@ -import 'package:cheminova/screens/home_screen.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:flutter/material.dart'; +import 'package:cheminova/screens/home_screen.dart'; // Importing HomePage screen +import 'package:cheminova/widgets/common_background.dart'; // Importing custom background widget +import 'package:flutter/material.dart'; // Flutter material package for UI components +// Stateful widget for the Attendance Success screen class AttendanceSuccess extends StatefulWidget { const AttendanceSuccess({super.key}); @@ -9,10 +10,13 @@ class AttendanceSuccess extends StatefulWidget { State createState() => _VerifySuccessFullScreenState(); } +// State implementation for AttendanceSuccess screen class _VerifySuccessFullScreenState extends State { + @override void initState() { super.initState(); + // Delayed navigation to HomePage after 2 seconds Future.delayed(const Duration(seconds: 2), () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage())); @@ -22,19 +26,22 @@ class _VerifySuccessFullScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( + // Using CommonBackground widget for consistent styling body: CommonBackground( child: Center( + // Centering content child: Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, // Vertically center elements children: [ + // Displaying confirmation message with styling Container( padding: const EdgeInsets.all(20.0), decoration: BoxDecoration( - color: const Color(0xFF243B8A), // Background color of the message box - borderRadius: BorderRadius.circular(15.0), + color: const Color(0xFF243B8A), // Message box background color + borderRadius: BorderRadius.circular(15.0), // Rounded corners for the message box ), child: const Text( - 'Your Attendance\nHas been\nMarked!', + 'Your Attendance\nHas been\nMarked!', // Text displayed in the center of the screen textAlign: TextAlign.center, style: TextStyle( fontSize: 36, @@ -44,8 +51,8 @@ class _VerifySuccessFullScreenState extends State { ), ), ), - const SizedBox(height: 20), // Add some space between the text and the image - // Image.asset('assets/check_circle.png'), + const SizedBox(height: 20), // Space between the text and image (image currently commented out) + // Image.asset('assets/check_circle.png'), // Image for success status (currently commented out) ], ), ), diff --git a/lib/screens/Update_inventorytask_screen.dart b/lib/screens/Update_inventorytask_screen.dart index 2c8d940..bfc16ce 100644 --- a/lib/screens/Update_inventorytask_screen.dart +++ b/lib/screens/Update_inventorytask_screen.dart @@ -17,68 +17,73 @@ class UpdateInventoryTaskScreen extends StatefulWidget { } class _UpdateInventoryTaskScreenState extends State { - late DailyTaskProvider _dailyTaskProvider; + late DailyTaskProvider _dailyTaskProvider; // Provider to manage daily tasks @override void initState() { super.initState(); - _dailyTaskProvider = DailyTaskProvider(); - apiCall(); - + _dailyTaskProvider = DailyTaskProvider(); // Initialize the provider + apiCall(); // Fetch tasks from the API } + // Function to fetch new and pending tasks void apiCall() async { - await _dailyTaskProvider.getTask(type: 'New',isAddPending: true); - await _dailyTaskProvider.getTask(type: 'Pending',isAddPending: true); + await _dailyTaskProvider.getTask(type: 'New', isAddPending: true); // Get new tasks + await _dailyTaskProvider.getTask(type: 'Pending', isAddPending: true); // Get pending tasks } - @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => _dailyTaskProvider, + create: (context) => _dailyTaskProvider, // Provide the DailyTaskProvider to the widget tree child: Scaffold( - extendBodyBehindAppBar: true, - appBar: _buildAppBar(), - drawer: const CommonDrawer(), + extendBodyBehindAppBar: true, // Extend the body behind the app bar + appBar: _buildAppBar(), // Build the app bar + drawer: const CommonDrawer(), // Navigation drawer body: CommonBackground( child: SafeArea( - child: _buildTaskList(), + child: _buildTaskList(), // Build the list of tasks ), ), ), ); } + // Function to build the app bar CommonAppBar _buildAppBar() { return CommonAppBar( - backgroundColor: Colors.transparent, - elevation: 0, + backgroundColor: Colors.transparent, // Transparent background for app bar + elevation: 0, // No shadow for app bar actions: [ + // Back button in the app bar IconButton( - onPressed: () => Navigator.pop(context), + onPressed: () => Navigator.pop(context), // Navigate back icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), ), ], title: const Text( 'Inventory Update Tasks', - style: TextStyle(color: Colors.black87, fontSize: 20), + style: TextStyle(color: Colors.black87, fontSize: 20), // Title style ), ); } + // Function to build the task list Widget _buildTaskList() { return Consumer( builder: (context, value, child) { + // Show loading indicator while fetching tasks if (value.isLoading) { return const Center(child: CircularProgressIndicator()); } + // Filter tasks that require inventory updates final inventoryTasks = value.newTasksList .where((task) => task.task?.toLowerCase() == 'update inventory data') .toList(); + // Show message if there are no inventory tasks if (inventoryTasks.isEmpty) { return const Center( child: Text( @@ -92,56 +97,64 @@ class _UpdateInventoryTaskScreenState extends State { ); } + // Build the list view for inventory tasks return ListView.separated( padding: const EdgeInsets.all(16), - itemCount: inventoryTasks.length, - separatorBuilder: (context, index) => const SizedBox(height: 8), - itemBuilder: (context, index) => _buildTaskCard(inventoryTasks[index]), + itemCount: inventoryTasks.length, // Number of tasks to display + separatorBuilder: (context, index) => const SizedBox(height: 8), // Space between items + itemBuilder: (context, index) => _buildTaskCard(inventoryTasks[index]), // Build each task card ); }, ); } + // Function to build an individual task card Widget _buildTaskCard(Tasks tasksList) { return InkWell( onTap: () { + // Navigate to AddProductsScreen when a task card is tapped if (tasksList.sId != null && tasksList.addedFor != null) { Navigator.push( - navigatorKey.currentContext!, - MaterialPageRoute( - builder: (context) => AddProductsScreen( - distributorType: tasksList.addedFor!, - inventoryId: tasksList.sId!, - tradeName: tasksList.tradeName??'', - pdRdId: tasksList.sId!))); + navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) => AddProductsScreen( + distributorType: tasksList.addedFor!, // Pass distributor type + inventoryId: tasksList.sId!, // Pass inventory ID + tradeName: tasksList.tradeName ?? '', // Pass trader name + pdRdId: tasksList.sId!, // Pass PD/RD ID + ), + ), + ); } }, child: Card( - color: Colors.white, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + color: Colors.white, // Card color + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), // Card shape child: ListTile( - leading: const Icon(Icons.inventory, color: Colors.blueAccent), + leading: const Icon(Icons.inventory, color: Colors.blueAccent), // Leading icon title: Text( - tasksList.task ?? '', + tasksList.task ?? '', // Task name style: const TextStyle( color: Colors.black87, fontWeight: FontWeight.w700, fontSize: 16, - fontFamily: 'Anek', + fontFamily: 'Anek', // Font style ), ), subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align subtitles to the start children: [ - Text('Distributor: ${tasksList.addedFor ?? ""}'), - Text('Trader Name: ${tasksList.tradeName??''}'), - if(tasksList.taskDueDate != null) Text('Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), - Text('Priority: ${tasksList.taskPriority}'), + Text('Distributor: ${tasksList.addedFor ?? ""}'), // Display distributor name + Text('Trader Name: ${tasksList.tradeName ?? ''}'), // Display trader name + // Display due date if it exists + if (tasksList.taskDueDate != null) + Text('Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), + Text('Priority: ${tasksList.taskPriority}'), // Display task priority ], ), - trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), // Trailing icon ), ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/add_sales_product_screen.dart b/lib/screens/add_sales_product_screen.dart index d69cf45..81b43a2 100644 --- a/lib/screens/add_sales_product_screen.dart +++ b/lib/screens/add_sales_product_screen.dart @@ -10,12 +10,14 @@ import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; class AddSalesProductScreen extends StatefulWidget { + // Final properties to store data passed from previous screen final String distributorType; final String tradeName; final String pdRdId; final String? inventoryId; const AddSalesProductScreen({ + super.key, required this.distributorType, required this.tradeName, @@ -28,6 +30,7 @@ class AddSalesProductScreen extends StatefulWidget { } class _AddSalesProductScreenState extends State { + // Controllers for text fields final searchController = TextEditingController(); late AddSalesProvider salesTaskProvider; final dateController = TextEditingController(); @@ -36,6 +39,7 @@ class _AddSalesProductScreenState extends State { @override void initState() { super.initState(); + // Initialize the provider and set the current date salesTaskProvider = Provider.of(context, listen: false); dateController.text = DateFormat('dd/MM/yyyy').format(DateTime.now()); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -45,6 +49,7 @@ class _AddSalesProductScreenState extends State { @override void dispose() { + // Dispose of the text controllers and reset the provider when the widget is removed searchController.dispose(); dateController.dispose(); if (mounted) { @@ -57,10 +62,10 @@ class _AddSalesProductScreenState extends State { Widget build(BuildContext context) { return PopScope( canPop: true, - child: CommonBackground( + child: CommonBackground(// Common background for the screen child: Scaffold( backgroundColor: Colors.transparent, - appBar: CommonAppBar( + appBar: CommonAppBar(// Custom AppBar with back button and title actions: [ IconButton( onPressed: () => Navigator.pop(context), @@ -94,7 +99,7 @@ class _AddSalesProductScreenState extends State { padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, - children: [ + children: [// Floating button to add products FloatingActionButton.extended( onPressed: () => _showProductSelectionBottomSheet(context), backgroundColor: Colors.white, @@ -103,6 +108,7 @@ class _AddSalesProductScreenState extends State { ), if (value.selectedProducts.isNotEmpty) ...[ const SizedBox(height: 16.0), + // Submit button that appears after products are selected CommonElevatedButton( borderRadius: 30, width: double.infinity, @@ -119,7 +125,7 @@ class _AddSalesProductScreenState extends State { ], ), ), - body: Consumer( + body: Consumer(// Displays the list of selected products or loading indicator builder: (context, value, child) { return Stack( children: [ @@ -127,7 +133,7 @@ class _AddSalesProductScreenState extends State { children: [ Padding( padding: const EdgeInsets.all(8.0), - child: TextFormField( + child: TextFormField(// Date selection field controller: dateController, readOnly: true, decoration: const InputDecoration( @@ -139,6 +145,7 @@ class _AddSalesProductScreenState extends State { ), ), ), + // Display selected products list if (value.selectedProducts.isNotEmpty) Expanded( child: ListView.builder( @@ -162,6 +169,7 @@ class _AddSalesProductScreenState extends State { ), ], ), + // Loading indicator if (value.isLoading) Container( color: Colors.black12, @@ -175,7 +183,7 @@ class _AddSalesProductScreenState extends State { ), ); } - +// Function to show product selection modal bottom sheet void _showProductSelectionBottomSheet(BuildContext context) { showModalBottomSheet( isScrollControlled: true, @@ -195,12 +203,13 @@ class _AddSalesProductScreenState extends State { border: OutlineInputBorder(), prefixIcon: Icon(Icons.search), ), - onChanged: (val) { + onChanged: (val) {// Filter product list based on search query value.filterProducts(val); setState(() {}); }, ), ), + // List of products to choose from Expanded( child: ListView.builder( itemCount: searchController.text.isEmpty @@ -239,9 +248,9 @@ class _AddSalesProductScreenState extends State { ), ); }, - ).whenComplete(() => setState(() {})); + ).whenComplete(() => setState(() {}));// Refresh UI after modal is closed } - +// Function to submit selected products void _submitProducts(AddSalesProvider value) { if (formKey.currentState!.validate()) { if (value.selectedProducts.isNotEmpty && @@ -257,7 +266,7 @@ class _AddSalesProductScreenState extends State { date: dateController.text.trim(), tradeName: widget.tradeName, ); - } else { + } else { // Show error message if any product information is missing ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Please fill out all product details, including sale and inventory.'), diff --git a/lib/screens/calendar_screen.dart b/lib/screens/calendar_screen.dart index 71d9cbf..7e54c80 100644 --- a/lib/screens/calendar_screen.dart +++ b/lib/screens/calendar_screen.dart @@ -1,42 +1,42 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; -import '../widgets/common_app_bar.dart'; -import '../widgets/common_background.dart'; -import '../widgets/common_drawer.dart'; +import '../widgets/common_app_bar.dart'; // Custom AppBar widget +import '../widgets/common_background.dart'; // Custom background widget +import '../widgets/common_drawer.dart'; // Custom drawer widget +// Global variables to track the focused and selected dates in the calendar +DateTime _focusedDay = DateTime.now(); +DateTime? _selectedDay = DateTime.now(); -DateTime _focusedDay=DateTime.now(); -DateTime? _selectedDay=DateTime.now(); - +// Main Calendar screen class CalendarScreen extends StatelessWidget { const CalendarScreen({super.key}); - @override Widget build(BuildContext context) { - return CommonBackground( + return CommonBackground( // Wrapper for common background styling child: Scaffold( - appBar: CommonAppBar( + appBar: CommonAppBar( // Custom AppBar actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - title: const Text('Calendar'), - backgroundColor: Colors.transparent, + IconButton( + onPressed: () { + Navigator.pop(context); // Go back to the previous screen + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: const Text('Calendar'), // Title of the AppBar + backgroundColor: Colors.transparent, // Transparent background for the AppBar elevation: 0, ), - backgroundColor: Colors.transparent, - drawer: const CommonDrawer(), - body: const SingleChildScrollView( + backgroundColor: Colors.transparent, // Transparent background for the Scaffold + drawer: const CommonDrawer(), // Custom drawer widget + body: const SingleChildScrollView( // Scrollable view to accommodate the calendar and events list child: Column( children: [ - CalendarWidget(), - EventsList(), + CalendarWidget(), // Calendar widget to show calendar dates + EventsList(), // List of events associated with the selected date ], ), ), @@ -45,6 +45,7 @@ class CalendarScreen extends StatelessWidget { } } +// Calendar widget that shows the calendar dates class CalendarWidget extends StatefulWidget { const CalendarWidget({super.key}); @@ -55,37 +56,37 @@ class CalendarWidget extends StatefulWidget { class _CalendarWidgetState extends State { @override Widget build(BuildContext context) { - return Card( - margin: const EdgeInsets.all(16), + return Card( // Card UI to display the calendar inside + margin: const EdgeInsets.all(16), // Margin around the card child: Padding( - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.all(16), // Padding inside the card child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left children: [ const Text( 'Calendar', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // Calendar heading ), const Text( 'Month/Year', - style: TextStyle(fontSize: 14, color: Colors.grey), + style: TextStyle(fontSize: 14, color: Colors.grey), // Display current month and year ), const SizedBox(height: 10), - TableCalendar( - firstDay: DateTime.utc(1900, 5, 1), - lastDay: DateTime.utc(2900, 5, 1), - focusedDay: _focusedDay, + TableCalendar( // TableCalendar widget to show date selection + firstDay: DateTime.utc(1900, 5, 1), // Earliest selectable date + lastDay: DateTime.utc(2900, 5, 1), // Latest selectable date + focusedDay: _focusedDay, // Currently focused day selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); + return isSameDay(_selectedDay, day); // Highlight selected day }, onDaySelected: (selectedDay, focusedDay) { - setState(() { + setState(() { // Update selected and focused days _selectedDay = selectedDay; _focusedDay = focusedDay; }); }, onPageChanged: (focusedDay) { - _focusedDay = focusedDay; + _focusedDay = focusedDay; // Change focused day when calendar page changes }, ), ], @@ -95,23 +96,25 @@ class _CalendarWidgetState extends State { } } +// Widget to display a list of events class EventsList extends StatelessWidget { const EventsList({super.key}); @override Widget build(BuildContext context) { - return const Card( - margin: EdgeInsets.all(16), + return const Card( // Card UI for the event list + margin: EdgeInsets.all(16), // Margin around the card child: Padding( - padding: EdgeInsets.all(16), + padding: EdgeInsets.all(16), // Padding inside the card child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left children: [ Text( - 'Events List', + 'Events List', // Heading for the events list style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), SizedBox(height: 10), + // Display individual event items EventItem( date: '10-06-2024', event: 'Meeting with Territory Manager', @@ -128,41 +131,42 @@ class EventsList extends StatelessWidget { } } +// Widget for displaying individual event items class EventItem extends StatelessWidget { - final String date; - final String event; + final String date; // Event date + final String event; // Event description const EventItem({super.key, required this.date, required this.event}); @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.all(12), + padding: const EdgeInsets.all(12), // Padding inside the event container decoration: BoxDecoration( - color: Colors.blue[50], - borderRadius: BorderRadius.circular(8), + color: Colors.blue[50], // Light blue background for the event item + borderRadius: BorderRadius.circular(8), // Rounded corners for the container ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left children: [ - Text('Date: $date'), + Text('Date: $date'), // Display event date Text( - 'Event: $event', + 'Event: $event', // Display event description style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - ElevatedButton( + ElevatedButton( // Button to view event details onPressed: () { // Add view details functionality here }, style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue[800], + foregroundColor: Colors.white, // Text color + backgroundColor: Colors.blue[800], // Button background color ), - child: const Text('VIEW DETAILS'), + child: const Text('VIEW DETAILS'), // Button label ), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/change_password_screen.dart b/lib/screens/change_password_screen.dart index 54e862b..ad43879 100644 --- a/lib/screens/change_password_screen.dart +++ b/lib/screens/change_password_screen.dart @@ -7,7 +7,6 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:cheminova/widgets/common_text_form_field.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; - import '../widgets/common_drawer.dart'; import 'home_screen.dart'; @@ -19,150 +18,155 @@ class ChangePasswordPage extends StatefulWidget { } class _ChangePasswordPageState extends State { - late ChangePasswordProvider changePasswordProvider; - final _formKey = GlobalKey(); + late ChangePasswordProvider changePasswordProvider; // Provider to manage state and logic + final _formKey = GlobalKey(); // Form key to manage form validation @override void initState() { - changePasswordProvider = ChangePasswordProvider(); + changePasswordProvider = ChangePasswordProvider(); // Initialize the provider super.initState(); } @override Widget build(BuildContext context) { + // Use ChangeNotifierProvider to listen to ChangePasswordProvider updates return ChangeNotifierProvider( create: (_) => changePasswordProvider, builder: (context, child) { - return Stack( + return Stack( // Stack to manage overlay loading indicator children: [ - CommonBackground( + CommonBackground( // Custom background widget isFullWidth: true, child: Scaffold( - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Custom drawer widget backgroundColor: Colors.transparent, appBar: CommonAppBar( title: const Text(''), - backgroundColor: Colors.transparent, + backgroundColor: Colors.transparent, // Transparent AppBar elevation: 0, actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Back button action }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), ), ], ), - body: SingleChildScrollView( + body: SingleChildScrollView( // Scrollable form child: Column( children: [ - // const SizedBox(height: 50), - // const SizedBox(height: 60), Container( - padding: const EdgeInsets.all(20.0) - .copyWith(top: 15, bottom: 30), + padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), margin: const EdgeInsets.symmetric(horizontal: 30.0), - decoration: BoxDecoration( + decoration: BoxDecoration( // Styling for the form container border: Border.all(color: Colors.white), color: const Color(0xffB4D1E5).withOpacity(0.9), borderRadius: BorderRadius.circular(26.0)), - child: Form( - key: _formKey, + child: Form( // Form widget to validate input fields + key: _formKey, // Reference to form state child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 20), - const Text('Change Password', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Roboto')), + const Text( + 'Change Password', + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w500, + fontFamily: 'Roboto', + ), + ), const SizedBox(height: 20), + + // Old Password Field Consumer( builder: (context, value, child) => CommonTextFormField( - controller: value.oldPasswordController, + controller: value.oldPasswordController, // Old password controller validator: (val) { if (val == null || val.isEmpty) { - return 'Please enter your password'; + return 'Please enter your password'; // Validation for empty input } return null; }, title: 'Old Password'), ), const SizedBox(height: 20), + + // New Password Field Consumer( builder: (context, value, child) => CommonTextFormField( - controller: value.newPasswordController, + controller: value.newPasswordController, // New password controller validator: (val) { if (val == null || val.isEmpty) { - return 'Please enter your password'; + return 'Please enter your password'; // Validation for empty input } return null; }, title: 'New Password'), ), const SizedBox(height: 20), + + // Confirm Password Field Consumer( builder: (context, value, child) => CommonTextFormField( - controller: - value.confirmPasswordController, + controller: value.confirmPasswordController, // Confirm password controller validator: (val) { if (val == null || val.isEmpty) { - return 'Please enter your password'; + return 'Please enter your password'; // Validation for empty input } else if (val != - value - .newPasswordController.text) { - return 'Password does not match'; + value.newPasswordController.text) { + return 'Password does not match'; // Validation for mismatched password } return null; }, title: 'Confirm Password'), ), const SizedBox(height: 15), + + // Submit button Align( - alignment: Alignment.center, - child: Consumer( - builder: (context, value, child) => - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SIGN IN', - isLoading: false, - onPressed: value.isLoading - ? null - : () async { - if (_formKey.currentState! - .validate()) { - value - .changePassword() - .then((result) { - var (status, message) = - result; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar( - content: - Text(message))); - if (status) { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => - const HomePage())); - } - }); + alignment: Alignment.center, + child: Consumer( + builder: (context, value, child) => + CommonElevatedButton( + backgroundColor: const Color(0xff004791), // Button styling + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SIGN IN', // Button text + isLoading: false, + onPressed: value.isLoading + ? null + : () async { + if (_formKey.currentState!.validate()) { + // If form is valid, proceed with password change + value.changePassword().then((result) { + var (status, message) = result; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)) + ); // Show success or error message + if (status) { + // Navigate to HomePage if password change is successful + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const HomePage())); } - }, - ), - )) + }); + } + }, + ), + ), + ) ], ), ), @@ -171,12 +175,15 @@ class _ChangePasswordPageState extends State { ), )), ), - Consumer( - builder: (context, value, child) => value.isLoading? - Container( - color: Colors.black.withOpacity(0.5), - child: const Center(child: CircularProgressIndicator())):Container(), - ) + + // Overlay loading indicator when processing + Consumer( + builder: (context, value, child) => value.isLoading + ? Container( + color: Colors.black.withOpacity(0.5), // Overlay background + child: const Center(child: CircularProgressIndicator())) // Show loading indicator + : Container(), + ) ], ); }); diff --git a/lib/screens/collect_kyc_screen.dart b/lib/screens/collect_kyc_screen.dart index 2dd45c1..e52d932 100644 --- a/lib/screens/collect_kyc_screen.dart +++ b/lib/screens/collect_kyc_screen.dart @@ -1,17 +1,17 @@ -import 'dart:io'; +import 'dart:io'; // Used for working with files import 'package:cheminova/provider/collect_kyc_provider.dart'; import 'package:cheminova/screens/retailer_details_screen.dart'; import 'package:cheminova/screens/upload_documents_screen.dart'; import 'package:cheminova/screens/verification_documents_screen.dart'; -import 'package:image_picker/image_picker.dart'; +import 'package:image_picker/image_picker.dart'; // For capturing and picking images import 'package:cheminova/widgets/common_drawer.dart'; import 'package:flutter/material.dart'; import 'package:cheminova/widgets/common_background.dart'; -import 'package:provider/provider.dart'; +import 'package:provider/provider.dart'; // For state management with Provider import '../widgets/common_app_bar.dart'; class CollectKycScreen extends StatefulWidget { - final String id; + final String id; // ID passed as a required parameter const CollectKycScreen({super.key, required this.id}); @override @@ -20,22 +20,23 @@ class CollectKycScreen extends StatefulWidget { class CollectKycScreenState extends State with SingleTickerProviderStateMixin { - late CollectKycProvider collectKycProvider; + late CollectKycProvider collectKycProvider; // Provider to handle business logic and state final _pages = [ - const RetailerDetailsScreen(), - const UploadDocumentScreen(), - const VerifyDocumentsScreen() + const RetailerDetailsScreen(), // First tab for Retailer Details + const UploadDocumentScreen(), // Second tab for uploading documents + const VerifyDocumentsScreen() // Third tab for verifying documents ]; @override void initState() { - collectKycProvider = CollectKycProvider(); - collectKycProvider.setId(widget.id); - collectKycProvider.tabController = TabController(length: 3, vsync: this); + collectKycProvider = CollectKycProvider(); // Initialize the provider + collectKycProvider.setId(widget.id); // Set the retailer ID in the provider + collectKycProvider.tabController = TabController(length: 3, vsync: this); // Initialize TabController for 3 tabs super.initState(); } + // Controllers for form fields final locationController = TextEditingController(); final notesController = TextEditingController(); final dealerController = TextEditingController(); @@ -48,38 +49,42 @@ class CollectKycScreenState extends State final aadharcardController = TextEditingController(); final pancardController = TextEditingController(); + // File variables for image picking File? _selfieImage; File? _pesticideLicenseImage; File? _fertilizerLicenseImage; - final ImagePicker _picker = ImagePicker(); + final ImagePicker _picker = ImagePicker(); // Image picker instance + // Function to pick image for selfie or documents Future _pickImage(ImageSource source, bool isSelfie) async { - final pickedFile = await _picker.pickImage(source: source); + final pickedFile = await _picker.pickImage(source: source); // Pick image from camera or gallery setState(() { if (pickedFile != null) { if (isSelfie) { - _selfieImage = File(pickedFile.path); + _selfieImage = File(pickedFile.path); // Set the picked file to the selfie image variable } } }); } + // Function to pick images for pesticide or fertilizer licenses Future _pickLicenseImage(ImageSource source, bool isPesticide) async { final pickedFile = await _picker.pickImage(source: source); setState(() { if (pickedFile != null) { if (isPesticide) { - _pesticideLicenseImage = File(pickedFile.path); + _pesticideLicenseImage = File(pickedFile.path); // Set the pesticide license image } else { - _fertilizerLicenseImage = File(pickedFile.path); + _fertilizerLicenseImage = File(pickedFile.path); // Set the fertilizer license image } } }); } + // Function to show image picker for selfie or document void _showPicker(BuildContext context, bool isSelfie) { showModalBottomSheet( context: context, @@ -91,14 +96,14 @@ class CollectKycScreenState extends State leading: const Icon(Icons.photo_library), title: const Text('Photo Library'), onTap: () { - _pickImage(ImageSource.gallery, isSelfie); + _pickImage(ImageSource.gallery, isSelfie); // Pick image from gallery Navigator.of(context).pop(); }), ListTile( leading: const Icon(Icons.photo_camera), title: const Text('Camera'), onTap: () { - _pickImage(ImageSource.camera, isSelfie); + _pickImage(ImageSource.camera, isSelfie); // Pick image from camera Navigator.of(context).pop(); }, ), @@ -108,6 +113,7 @@ class CollectKycScreenState extends State }); } + // Function to show picker for licenses void _showLicensePicker(BuildContext context, bool isPesticide) { showModalBottomSheet( context: context, @@ -119,14 +125,14 @@ class CollectKycScreenState extends State leading: const Icon(Icons.photo_library), title: const Text('Photo Library'), onTap: () { - _pickLicenseImage(ImageSource.gallery, isPesticide); + _pickLicenseImage(ImageSource.gallery, isPesticide); // Pick image for license from gallery Navigator.of(context).pop(); }), ListTile( leading: const Icon(Icons.photo_camera), title: const Text('Camera'), onTap: () { - _pickLicenseImage(ImageSource.camera, isPesticide); + _pickLicenseImage(ImageSource.camera, isPesticide); // Pick image for license from camera Navigator.of(context).pop(); }, ), @@ -139,63 +145,64 @@ class CollectKycScreenState extends State @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => collectKycProvider, + create: (context) => collectKycProvider, // Provide the state management child: Consumer( builder: (BuildContext context, value, Widget? child) => Stack(children: [ - CommonBackground( - child: DefaultTabController( - length: 3, - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: PreferredSize( - preferredSize: const Size.fromHeight(100), - child: CommonAppBar( - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: - Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - title: const Text('Collect KYC Data', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - backgroundColor: Colors.transparent, - elevation: 0, - bottom: TabBar( - controller: value.tabController, - padding: const EdgeInsets.symmetric( - horizontal: 10), - dividerColor: Colors.transparent, - indicatorColor: Colors.yellow, - labelColor: Colors.white, - unselectedLabelColor: Colors.black, - indicatorSize: TabBarIndicatorSize.tab, - indicator: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(10)), - tabs: const [ - Tab(text: 'Details'), - Tab(text: 'Documents'), - Tab(text: 'Verify') - ]))), - drawer: const CommonDrawer(), - body: TabBarView( - controller: value.tabController, - physics: const NeverScrollableScrollPhysics(), - children: _pages)))), - if (value.isLoading) - Container( - color: Colors.black.withOpacity(0.5), - child: const Center(child: CircularProgressIndicator())) - ]), + CommonBackground( + child: DefaultTabController( + length: 3, // Number of tabs + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(100), // Custom AppBar with Tabs + child: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + Navigator.pop(context); // Back button to navigate back + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: const Text('Collect KYC Data', // Title for the AppBar + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, + bottom: TabBar( // TabBar for navigation between the pages + controller: value.tabController, // Use the TabController from provider + padding: const EdgeInsets.symmetric(horizontal: 10), + dividerColor: Colors.transparent, + indicatorColor: Colors.yellow, + labelColor: Colors.white, + unselectedLabelColor: Colors.black, + indicatorSize: TabBarIndicatorSize.tab, + indicator: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(10)), // Custom styling for selected tab + tabs: const [ + Tab(text: 'Details'), // First tab + Tab(text: 'Documents'), // Second tab + Tab(text: 'Verify') // Third tab + ]))), + + drawer: const CommonDrawer(), // Drawer for navigation + body: TabBarView( // View for each tab + controller: value.tabController, + physics: const NeverScrollableScrollPhysics(), // Disable swipe gesture for tab switch + children: _pages)))), + + // Loading indicator overlay when `isLoading` is true + if (value.isLoading) + Container( + color: Colors.black.withOpacity(0.5), // Semi-transparent overlay + child: const Center(child: CircularProgressIndicator())) // Show loading spinner + ]), ), ); } diff --git a/lib/screens/daily_tasks_screen.dart b/lib/screens/daily_tasks_screen.dart index d4acd35..36d1a53 100644 --- a/lib/screens/daily_tasks_screen.dart +++ b/lib/screens/daily_tasks_screen.dart @@ -12,6 +12,8 @@ import 'package:provider/provider.dart'; import '../provider/daily_task_provider.dart'; +/// This widget represents the Daily Tasks screen which is +/// divided into three categories: New, Pending, and Completed. class DailyTasksScreen extends StatefulWidget { const DailyTasksScreen({super.key}); @@ -20,10 +22,11 @@ class DailyTasksScreen extends StatefulWidget { } class _DailyTasksScreenState extends State { - final List _tabTitles = ['NEW', 'PENDING', 'COMPLETED']; - int _selectedTabIndex = 0; + final List _tabTitles = ['NEW', 'PENDING', 'COMPLETED']; // Tab labels + int _selectedTabIndex = 0; // Current selected tab index late DailyTaskProvider _dailyTaskProvider; + /// Initial setup for getting the initial data (New tasks). @override void initState() { _dailyTaskProvider = DailyTaskProvider(); @@ -31,6 +34,7 @@ class _DailyTasksScreenState extends State { super.initState(); } + /// The build function returns the widget tree for the DailyTasksScreen. @override Widget build(BuildContext context) { return ChangeNotifierProvider( @@ -44,9 +48,9 @@ class _DailyTasksScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildCustomTabBar(), + _buildCustomTabBar(), // Custom tab bar for task categories Expanded( - child: _buildTaskList(_selectedTabIndex), + child: _buildTaskList(_selectedTabIndex), // Task list view based on selected tab ), ], ), @@ -55,6 +59,7 @@ class _DailyTasksScreenState extends State { )); } + /// Builds the app bar with a back button and the title 'Daily Tasks'. CommonAppBar _buildAppBar() { return CommonAppBar( backgroundColor: Colors.transparent, @@ -73,6 +78,7 @@ class _DailyTasksScreenState extends State { ); } + /// Custom tab bar at the top of the screen to switch between task categories. Widget _buildCustomTabBar() { return Container( height: 60, @@ -82,6 +88,7 @@ class _DailyTasksScreenState extends State { children: List.generate(_tabTitles.length, (index) { return GestureDetector( onTap: () { + // Update selected tab index and fetch relevant task data setState(() => _selectedTabIndex = index); if (index == 0) { _dailyTaskProvider.getTask(type: 'New'); @@ -96,10 +103,10 @@ class _DailyTasksScreenState extends State { decoration: BoxDecoration( gradient: _selectedTabIndex == index ? const LinearGradient( - colors: [Color(0xff004791), Color(0xff1a73e8)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ) + colors: [Color(0xff004791), Color(0xff1a73e8)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) : null, color: _selectedTabIndex == index ? Colors.transparent @@ -144,6 +151,7 @@ class _DailyTasksScreenState extends State { ); } + /// Provides appropriate icons for the tabs. IconData _getTabIcon(int index) { switch (index) { case 0: @@ -157,78 +165,81 @@ class _DailyTasksScreenState extends State { } } + /// Builds the task list according to the selected tab. Widget _buildTaskList(int tabIndex) { return Consumer( builder: (context, value, child) => value.isLoading ? const Center(child: CircularProgressIndicator()) : ListView.separated( - padding: const EdgeInsets.all(16), - itemCount: _selectedTabIndex == 0 - ? value.newTasksList.length - : _selectedTabIndex == 1 - ? value.pendingTasksList.length - : value.completedTasksList.length, - separatorBuilder: (context, index) => const SizedBox(height: 8), - itemBuilder: (context, index) { - final tasksList = tabIndex == 0 - ? value.newTasksList - : tabIndex == 1 - ? value.pendingTasksList - : value.completedTasksList; - return _buildTaskCard(tasksList[index]); - }, - ), + padding: const EdgeInsets.all(16), + itemCount: _selectedTabIndex == 0 + ? value.newTasksList.length + : _selectedTabIndex == 1 + ? value.pendingTasksList.length + : value.completedTasksList.length, + separatorBuilder: (context, index) => const SizedBox(height: 8), + itemBuilder: (context, index) { + final tasksList = tabIndex == 0 + ? value.newTasksList + : tabIndex == 1 + ? value.pendingTasksList + : value.completedTasksList; + return _buildTaskCard(tasksList[index]); + }, + ), ); } + /// Builds a task card widget based on the task details. Widget _buildTaskCard(Tasks tasksList) { return InkWell( onTap: _selectedTabIndex == 2 ? null // Disable click when on the "COMPLETED" tab : () { print('Task: ${tasksList.toJson()}'); - if (tasksList.task == 'Collect KYC') { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - CollectKycScreen(id: tasksList.sId ?? ''))); - } else if (tasksList.task == 'REUPLOAD') { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - CollectKycScreen(id: tasksList.taskId ?? ''))); - } else if (tasksList.task == 'Update Inventory Data') { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AddProductsScreen( - distributorType: tasksList.addedFor!, - tradeName: tasksList.tradeName ?? '', - pdRdId: tasksList.addedForId!, - inventoryId: tasksList.sId))); - } else if (tasksList.task == 'Update Sales Data') { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AddSalesProductScreen( - distributorType: tasksList.addedFor!, - tradeName: tasksList.tradeName!, - pdRdId: tasksList.addedForId!, - inventoryId:tasksList.sId, - ))); - } else if (tasksList.task == 'Visit RD/PD') { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => VisitDealersScreen( - tradeName: tasksList.tradeName ?? '', - id: tasksList.sId, - type: tasksList.addedFor, - ))); - } - }, + // Navigation to respective screens based on task type + if (tasksList.task == 'Collect KYC') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CollectKycScreen(id: tasksList.sId ?? ''))); + } else if (tasksList.task == 'REUPLOAD') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CollectKycScreen(id: tasksList.taskId ?? ''))); + } else if (tasksList.task == 'Update Inventory Data') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddProductsScreen( + distributorType: tasksList.addedFor!, + tradeName: tasksList.tradeName ?? '', + pdRdId: tasksList.addedForId!, + inventoryId: tasksList.sId))); + } else if (tasksList.task == 'Update Sales Data') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddSalesProductScreen( + distributorType: tasksList.addedFor!, + tradeName: tasksList.tradeName!, + pdRdId: tasksList.addedForId!, + inventoryId: tasksList.sId, + ))); + } else if (tasksList.task == 'Visit RD/PD') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => VisitDealersScreen( + tradeName: tasksList.tradeName ?? '', + id: tasksList.sId, + type: tasksList.addedFor, + ))); + } + }, child: Card( color: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), diff --git a/lib/screens/data_submit_successfull.dart b/lib/screens/data_submit_successfull.dart index 32dffb0..8d530b7 100644 --- a/lib/screens/data_submit_successfull.dart +++ b/lib/screens/data_submit_successfull.dart @@ -1,7 +1,8 @@ -import 'package:cheminova/screens/home_screen.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:flutter/material.dart'; +import 'package:cheminova/screens/home_screen.dart'; // Import the home screen for navigation after submission +import 'package:cheminova/widgets/common_background.dart'; // Import custom background widget +import 'package:flutter/material.dart'; // Import Flutter material package +// Create a StatefulWidget for the success screen after data submission class DataSubmitSuccessFullScreen extends StatefulWidget { const DataSubmitSuccessFullScreen({super.key}); @@ -9,30 +10,41 @@ class DataSubmitSuccessFullScreen extends StatefulWidget { State createState() => DataSubmitSuccessFullScreenState(); } +// Define the state class for the DataSubmitSuccessFullScreen class DataSubmitSuccessFullScreenState extends State { @override void initState() { super.initState(); + // Navigate to the HomePage after a 2-second delay Future.delayed(const Duration(seconds: 2), () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage())); }); } + @override Widget build(BuildContext context) { - return Scaffold( - body: CommonBackground( - child: Center( + // Build the UI for the success screen + return Scaffold( + body: CommonBackground( // Use the custom background widget + child: Center( // Center the content vertically and horizontally child: Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, // Center items in the column children: [ + // Display a success message const Text('Data Submitted\nsuccessfully', style: TextStyle( - fontSize: 36, - color: Colors.white, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), + fontSize: 36, // Set font size + color: Colors.white, // Set font color + fontWeight: FontWeight.w400, // Set font weight + fontFamily: 'Anek')), // Set font family const SizedBox(height: 20), // Add some space between the text and the image - Image.asset('assets/check_circle.png', )])))); + // Display a success image + Image.asset('assets/check_circle.png',) // Load the success image + ] + ) + ) + ) + ); } } diff --git a/lib/screens/forgot_password_screen.dart b/lib/screens/forgot_password_screen.dart index 00eeb07..0239cbb 100644 --- a/lib/screens/forgot_password_screen.dart +++ b/lib/screens/forgot_password_screen.dart @@ -1,14 +1,15 @@ -import 'package:cheminova/provider/forgot_password_provider.dart'; -import 'package:cheminova/screens/change_password_screen.dart'; -import 'package:cheminova/screens/login_screen.dart'; -import 'package:cheminova/screens/password_change_screen.dart'; -import 'package:cheminova/screens/verify_code_screen.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:cheminova/widgets/common_elevated_button.dart'; -import 'package:cheminova/widgets/common_text_form_field.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:cheminova/provider/forgot_password_provider.dart'; // Import the provider for handling forgot password logic +import 'package:cheminova/screens/change_password_screen.dart'; // Import screen for changing password +import 'package:cheminova/screens/login_screen.dart'; // Import the login screen +import 'package:cheminova/screens/password_change_screen.dart'; // Import screen for password change +import 'package:cheminova/screens/verify_code_screen.dart'; // Import screen for verifying code +import 'package:cheminova/widgets/common_background.dart'; // Import custom background widget +import 'package:cheminova/widgets/common_elevated_button.dart'; // Import custom elevated button widget +import 'package:cheminova/widgets/common_text_form_field.dart'; // Import custom text form field widget +import 'package:flutter/material.dart'; // Import Flutter material package +import 'package:provider/provider.dart'; // Import provider package for state management +// Create a StatefulWidget for the Forgot Password screen class ForgotPasswordScreen extends StatefulWidget { const ForgotPasswordScreen({super.key}); @@ -16,29 +17,36 @@ class ForgotPasswordScreen extends StatefulWidget { State createState() => _ForgotPasswordScreenState(); } +// Define the state class for the ForgotPasswordScreen class _ForgotPasswordScreenState extends State { - final _formKey = GlobalKey(); - late ForgotPasswordProvider forgotPasswordProvider; + final _formKey = GlobalKey(); // Key to identify the form + late ForgotPasswordProvider forgotPasswordProvider; // Provider for managing forgot password state @override void initState() { - forgotPasswordProvider = ForgotPasswordProvider(); super.initState(); + forgotPasswordProvider = ForgotPasswordProvider(); // Initialize the provider } @override Widget build(BuildContext context) { - + // Use ChangeNotifierProvider to provide ForgotPasswordProvider to the widget tree return ChangeNotifierProvider( - create: (_) => forgotPasswordProvider, - builder: (context, child) { + create: (_) => forgotPasswordProvider, + builder: (context, child) { return CommonBackground( isFullWidth: false, child: Scaffold( backgroundColor: Colors.transparent, - appBar: AppBar(leading: InkWell(onTap:() { - Navigator.pop(context); - },child: Image.asset('assets/Back_attendance.png')),backgroundColor: Colors.transparent,), + appBar: AppBar( + leading: InkWell( + onTap: () { + Navigator.pop(context); // Navigate back when the back button is pressed + }, + child: Image.asset('assets/Back_attendance.png'), + ), + backgroundColor: Colors.transparent, + ), body: Center( child: SingleChildScrollView( child: Container( @@ -50,19 +58,20 @@ class _ForgotPasswordScreenState extends State { borderRadius: BorderRadius.circular(26.0), ), child: Form( - key: _formKey, + key: _formKey, // Assign the form key child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ + // Logo displayed at the top Align( - alignment: Alignment.topLeft, - child: Image.asset( - 'assets/lock_logo2.png', - height: 50.0, // Adjust the height as needed - width: 50.0, // Adjust the width as needed - ), + alignment: Alignment.topLeft, + child: Image.asset( + 'assets/lock_logo2.png', + height: 50.0, // Set height for the logo + width: 50.0, // Set width for the logo + ), ), const Text( 'Forgot Password', @@ -83,56 +92,62 @@ class _ForgotPasswordScreenState extends State { ), ), const SizedBox(height: 20), + // Text field for entering email Consumer( builder: (context, value, child) => CommonTextFormField( - controller: value.emailController, - title: ' Enter Your Email ID'), + controller: value.emailController, // Controller for the email input + title: ' Enter Your Email ID', + ), ), const SizedBox(height: 20), + // Button to navigate back to the login screen Align( alignment: Alignment.center, child: TextButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Navigate back to login }, - child: const Text('Back to Login', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Roboto')), + child: const Text( + 'Back to Login', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Roboto', + ), + ), ), ), const SizedBox(height: 15), + // Button to send the password reset request Align( alignment: Alignment.center, child: Consumer( - builder: (context, value, child) => CommonElevatedButton( - backgroundColor: const Color(0xff004791), + builder: (context, value, child) => CommonElevatedButton( + backgroundColor: const Color(0xff004791), // Button background color borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, - text: 'SEND', - onPressed: value.isLoading + text: 'SEND', // Button text + onPressed: value.isLoading // Disable button if loading ? null : () async { - if (_formKey.currentState!.validate()) { + if (_formKey.currentState!.validate()) { // Validate the form + // Call the forgot password method and handle the result value.forgotPassword().then((result) { - var (status, message) = result; + var (status, message) = result; // Destructure the result ScaffoldMessenger.of(context) .showSnackBar(SnackBar( - content: Text(message))); - if (status) { + content: Text(message))); // Show snackbar with message + if (status) { // Navigate to login page if successful Navigator.pushReplacement( context, MaterialPageRoute( - builder: (context) => - const LoginPage())); + builder: (context) => const LoginPage())); } }); } }, - ), ), ), @@ -144,7 +159,7 @@ class _ForgotPasswordScreenState extends State { ), ), ); - } + }, ); } } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 97105ff..8d21701 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -21,21 +21,22 @@ class HomePage extends StatefulWidget { const HomePage({super.key}); @override - State createState() => _HomePageState(); + State createState() => _HomePageState(); // Create state for HomePage } class _HomePageState extends State { - NotificationServices notificationServices = NotificationServices(); + NotificationServices notificationServices = NotificationServices(); // Initialize notification services @override void initState() { super.initState(); + // After the first frame is rendered, perform the following actions WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Provider.of(context, listen: false).getProfile(); - notificationServices.requestNotificationPermission(); + Provider.of(context, listen: false).getProfile(); // Fetch user profile data + notificationServices.requestNotificationPermission(); // Request notification permissions notificationServices.getDeviceToken().then((value) { if (kDebugMode) { - print('Device Token: $value'); + print('Device Token: $value'); // Print device token in debug mode } }); }); @@ -55,31 +56,32 @@ class _HomePageState extends State { color: Colors.black87, fontSize: 14, fontWeight: FontWeight.w400)), - Consumer( - builder: (context, value, child) => - (value.profileResponse == null || - value.profileResponse!.myData == null || - value.profileResponse!.myData!.name == null) - ? const SizedBox() - : Text(value.profileResponse!.myData!.name ?? '', - style: const TextStyle( - color: Colors.black87, fontSize: 20))) + // Display user's name from profile data if available + Consumer(builder: (context, value, child) => + (value.profileResponse == null || + value.profileResponse!.myData == null || + value.profileResponse!.myData!.name == null) + ? const SizedBox() + : Text(value.profileResponse!.myData!.name ?? '', + style: const TextStyle( + color: Colors.black87, fontSize: 20))) ]) ]), backgroundColor: Colors.transparent, - elevation: 0, + elevation: 0, // Remove elevation from the app bar ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Add custom drawer body: SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), child: LayoutBuilder( builder: (context, constraints) { - double screenWidth = constraints.maxWidth; - double buttonWidth = screenWidth / 2 - 18; // Adjust button width + double screenWidth = constraints.maxWidth; // Get screen width + double buttonWidth = screenWidth / 2 - 18; // Calculate button width return ListView( children: [ + // Create custom card for marking attendance _buildCustomCard( 'Mark Attendance', 'Mark Attendance / On Leave', @@ -94,6 +96,7 @@ class _HomePageState extends State { }, ), const SizedBox(height: 5), + // Create custom card for daily tasks _buildCustomCard( 'Daily Tasks', 'Dashboard', @@ -108,6 +111,7 @@ class _HomePageState extends State { }, ), const SizedBox(height: 5), + // Row containing cards for updating sales data and inventory Row( children: [ Expanded( @@ -119,8 +123,7 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - SalesTaskScreen() + builder: (context) => SalesTaskScreen(), ), ); }, @@ -136,8 +139,7 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - const UpdateInventoryTaskScreen(), + builder: (context) => const UpdateInventoryTaskScreen(), ), ); }, @@ -146,6 +148,7 @@ class _HomePageState extends State { ], ), const SizedBox(height: 5), + // Row containing cards for notifications and product purchase data Row( children: [ Expanded( @@ -157,8 +160,7 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - const NotificationScreen(), + builder: (context) => const NotificationScreen(), ), ); }, @@ -174,8 +176,7 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - const ProductPurchaseData(), + builder: (context) => const ProductPurchaseData(), ), ); }, @@ -184,6 +185,7 @@ class _HomePageState extends State { ], ), const SizedBox(height: 5), + // Create custom card for collecting KYC data _buildCustomCard( 'Collect \nKYC Data', 'Scan and upload KYC Documents', @@ -198,6 +200,7 @@ class _HomePageState extends State { }, ), const SizedBox(height: 5), + // Create custom card for rejected applications _buildCustomCard( 'Rejected Applications', 'Re-upload Rejected Documents', @@ -206,13 +209,13 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - const RejectedApplicationScreen(), + builder: (context) => const RejectedApplicationScreen(), ), ); }, ), const SizedBox(height: 5), + // Row containing cards for calendar and products manual Row( children: [ Expanded( @@ -240,8 +243,7 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - const ProductsManualScreen(), + builder: (context) => const ProductsManualScreen(), ), ); }, @@ -259,21 +261,22 @@ class _HomePageState extends State { ); } + // Helper method to build a custom card with title, subtitle, and tap action Widget _buildCustomCard(String title, String subtitle, double width, {void Function()? onTap}) { return Container( width: width, - margin: const EdgeInsets.only(bottom: 10), + margin: const EdgeInsets.only(bottom: 10), // Add margin at the bottom decoration: BoxDecoration( - color: Colors.indigo, - border: Border.all(color: Colors.white), - borderRadius: BorderRadius.circular(10), + color: Colors.indigo, // Set card background color + border: Border.all(color: Colors.white), // Set border color + borderRadius: BorderRadius.circular(10), // Set border radius ), child: ListTile( title: Text( title, style: const TextStyle( - color: Colors.white, + color: Colors.white, // Set title text color fontWeight: FontWeight.w800, fontSize: 18, fontFamily: 'Anek'), @@ -281,10 +284,10 @@ class _HomePageState extends State { subtitle: subtitle.isNotEmpty ? Text( subtitle, - style: const TextStyle(color: Colors.white70, fontSize: 13), + style: const TextStyle(color: Colors.white70, fontSize: 13), // Set subtitle text color ) : null, - onTap: onTap, + onTap: onTap, // Assign tap action ), ); } diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 696b498..caa0c24 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -13,168 +13,164 @@ class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override - State createState() => _LoginPageState(); + State createState() => _LoginPageState(); // Create state for LoginPage } class _LoginPageState extends State { - late LoginProvider loginProvider; - final _formKey = GlobalKey(); + late LoginProvider loginProvider; // Declare LoginProvider + final _formKey = GlobalKey(); // Global key for form validation @override void initState() { - loginProvider = LoginProvider(); + loginProvider = LoginProvider(); // Initialize LoginProvider super.initState(); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider( + return ChangeNotifierProvider( // Provide LoginProvider to the widget tree create: (_) => loginProvider, builder: (context, child) => Scaffold( body: CommonBackground( - isFullWidth: false, - child: Center( - child: SingleChildScrollView( - child: Column( - children: [ - const Text('Welcome', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w400, - color: Colors.white, - fontFamily: 'Roboto')), - const SizedBox(height: 50), - Align( - alignment: Alignment.center, - child: Container(decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12)), - padding: const EdgeInsets.all(8), - child: Image.asset('assets/cheminova_logo.png', - height: kToolbarHeight - 25), - ), - ), - const SizedBox(height: 60), - Container( - padding: - const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), - child: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const SizedBox(height: 20), - const Text('Login', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Roboto')), - const Text('Sign in to continue', - style: TextStyle( - fontSize: 14, - color: Colors.black, - fontWeight: FontWeight.w300, - fontFamily: 'Roboto')), - const SizedBox(height: 20), - Consumer( - builder: (context, value, child) => - CommonTextFormField( - controller: value.emailController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your email id'; - } - if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$') - .hasMatch(value)) { - return 'Please enter a valid email id'; - } - return null; - }, - title: 'Username')), - const SizedBox(height: 20), - Consumer( - builder: (context, value, child) => - CommonTextFormField( - obscureText: true, - controller: value.passwordController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your password'; - } - return null; - }, - title: 'Password')), - const SizedBox(height: 15), - - Align( - alignment: Alignment.center, - child: TextButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const ForgotPasswordScreen())); - }, - child: const Text('Forgot Password?', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Roboto')), - ), - ), - const SizedBox(height: 15), - Align( - alignment: Alignment.center, - child: Consumer( - builder: (context, value, child) => - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SIGN IN', - isLoading: value.isLoading, - // onPressed: () { - // Navigator.push(context, MaterialPageRoute(builder:(context) => const VerifyPhoneScreen())); - // }, - onPressed: value.isLoading - ? null - : () async { - if (_formKey.currentState!.validate()) { - value.login().then((result) { - var (status, message) = result; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar( - content: Text(message))); - if (status) { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => - const HomePage())); - } - }); - } - }, - ), - )) - ], + isFullWidth: false, + child: Center( + child: SingleChildScrollView( // Allow scrolling for the content + child: Column( + children: [ + const Text('Welcome', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w400, + color: Colors.white, + fontFamily: 'Roboto')), + const SizedBox(height: 50), // Spacer + Align( + alignment: Alignment.center, + child: Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), // Styling for logo container + padding: const EdgeInsets.all(8), + child: Image.asset('assets/cheminova_logo.png', height: kToolbarHeight - 25), // Display logo + ), ), - ), + const SizedBox(height: 60), // Spacer + Container( + padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), // Padding for login form + margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50), // Margin for login form + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.9), + borderRadius: BorderRadius.circular(26.0)), // Styling for form container + child: Form( + key: _formKey, // Assign global key for form validation + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 20), // Spacer + const Text('Login', + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w500, + fontFamily: 'Roboto')), + const Text('Sign in to continue', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.w300, + fontFamily: 'Roboto')), + const SizedBox(height: 20), // Spacer + Consumer( // Listen for changes in LoginProvider + builder: (context, value, child) => + CommonTextFormField( + controller: value.emailController, // Email input field + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your email id'; // Validation message + } + if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$') + .hasMatch(value)) { + return 'Please enter a valid email id'; // Validation message for invalid email + } + return null; + }, + title: 'Username')), // Field title + const SizedBox(height: 20), // Spacer + Consumer( // Listen for changes in LoginProvider + builder: (context, value, child) => + CommonTextFormField( + obscureText: true, // Hide password input + controller: value.passwordController, // Password input field + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; // Validation message + } + return null; + }, + title: 'Password')), // Field title + const SizedBox(height: 15), // Spacer + + Align( + alignment: Alignment.center, + child: TextButton( // Button for forgot password + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const ForgotPasswordScreen())); // Navigate to forgot password screen + }, + child: const Text('Forgot Password?', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Roboto')), + ), + ), + const SizedBox(height: 15), // Spacer + Align( + alignment: Alignment.center, + child: Consumer( // Listen for changes in LoginProvider + builder: (context, value, child) => + CommonElevatedButton( + backgroundColor: const Color(0xff004791), // Button color + borderRadius: 30, + width: double.infinity, // Full width button + height: kToolbarHeight - 10, + text: 'SIGN IN', // Button text + isLoading: value.isLoading, // Show loading indicator if true + onPressed: value.isLoading // Disable button if loading + ? null + : () async { + if (_formKey.currentState!.validate()) { // Validate the form + value.login().then((result) { // Call login method + var (status, message) = result; // Destructure result + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: Text(message))); // Show message in snackbar + if (status) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const HomePage())); // Navigate to home screen if successful + } + }); + } + }, + ), + )), + ], + ), + ), + ), + ], ), - ], + ), ), - ), - ), - )), + )), ); } } diff --git a/lib/screens/mark_attendence_screen.dart b/lib/screens/mark_attendence_screen.dart index bb56aca..12fb6cb 100644 --- a/lib/screens/mark_attendence_screen.dart +++ b/lib/screens/mark_attendence_screen.dart @@ -11,77 +11,79 @@ import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; + class MarkAttendanceScreen extends StatefulWidget { const MarkAttendanceScreen({super.key}); @override - State createState() => _MarkAttendanceScreenState(); + State createState() => _MarkAttendanceScreenState(); // Create state for the screen } class _MarkAttendanceScreenState extends State { - late AttendanceProvider attendanceProvider; + late AttendanceProvider attendanceProvider; // Attendance provider instance final dateController = TextEditingController( - text: DateFormat('yyyy/MM/dd').format(DateTime.now())); + text: DateFormat('yyyy/MM/dd').format(DateTime.now())); // Initialize date controller with the current date - final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + final timeController = TextEditingController( + text: DateFormat('hh:mm a').format(DateTime.now())); // Initialize time controller with the current time - final locationController = TextEditingController(); - final notesController = TextEditingController(); + final locationController = TextEditingController(); // Location controller for the user's current location + final notesController = TextEditingController(); // Notes controller @override void initState() { super.initState(); - attendanceProvider=AttendanceProvider(); - _getCurrentLocation(); + attendanceProvider = AttendanceProvider(); // Initialize the attendance provider + _getCurrentLocation(); // Fetch the user's current location } Future _getCurrentLocation() async { - LocationPermission permission = await Geolocator.checkPermission(); + LocationPermission permission = await Geolocator.checkPermission(); // Check location permission if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); + permission = await Geolocator.requestPermission(); // Request permission if denied if (permission == LocationPermission.denied) { - debugPrint('location denied'); + debugPrint('location denied'); // Log message if location permission is denied return; } } if (permission == LocationPermission.deniedForever) { setState(() { - debugPrint('Location permissions are permanently denied'); + debugPrint('Location permissions are permanently denied'); // Log if permissions are permanently denied }); return; } - Position position = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high); + // Fetch user's current location with high accuracy + Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); + debugPrint('position--- $position'); // Log position coordinates - debugPrint('position--- $position'); + // Get the placemark (location information) from the coordinates + List placeMarks = await placemarkFromCoordinates( + position.latitude, position.longitude) + .timeout(const Duration(seconds: 10)); // Set a timeout for location request + debugPrint('place mark--- $placeMarks'); // Log the placemark details - List placeMarks = - await placemarkFromCoordinates(position.latitude, position.longitude) - .timeout(const Duration(seconds: 10)); - debugPrint('place mark--- $placeMarks'); - - Placemark place = placeMarks[0]; + Placemark place = placeMarks[0]; // Use the first placemark setState(() { - locationController.text = place.locality??''; + locationController.text = place.locality ?? ''; // Set locality to locationController }); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider( + return ChangeNotifierProvider( // Provide attendance provider to widget tree create: (_) => attendanceProvider, builder: (context, child) => CommonBackground( - child: Scaffold(backgroundColor: Colors.transparent, + child: Scaffold( + backgroundColor: Colors.transparent, appBar: CommonAppBar( actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Go back when the button is pressed }, - icon: Image.asset('assets/Back_attendance.png'), + icon: Image.asset('assets/Back_attendance.png'), // Back icon padding: const EdgeInsets.only(right: 20), ), ], @@ -90,28 +92,29 @@ class _MarkAttendanceScreenState extends State { fontSize: 28, color: Colors.black, fontWeight: FontWeight.w400, - fontFamily: 'Anek')), backgroundColor: Colors.transparent, elevation: 0, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Common drawer body: Stack( children: [ Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), // Smooth scrolling behavior child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 16), Container( - padding: - const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), + padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), margin: const EdgeInsets.symmetric(horizontal: 20.0), decoration: BoxDecoration( border: Border.all(color: Colors.white), color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), + borderRadius: BorderRadius.circular(26.0)), // Styling container child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -119,26 +122,26 @@ class _MarkAttendanceScreenState extends State { title: 'Date', readOnly: true, fillColor: Colors.white, - controller: dateController), + controller: dateController), // Date input field (read-only) const SizedBox(height: 15), CommonTextFormField( title: 'Time', readOnly: true, fillColor: Colors.white, - controller: timeController), + controller: timeController), // Time input field (read-only) const SizedBox(height: 15), CommonTextFormField( title: 'Location', readOnly: true, fillColor: Colors.white, - controller: locationController), + controller: locationController), // Location input field (read-only) const SizedBox(height: 15), CommonTextFormField( height: 100, title: 'Notes', fillColor: Colors.white, maxLines: 4, - controller: notesController), + controller: notesController), // Notes input field (editable) const SizedBox(height: 16), Align( alignment: Alignment.center, @@ -146,8 +149,8 @@ class _MarkAttendanceScreenState extends State { children: [ Consumer( builder: (BuildContext context, - AttendanceProvider value, - Widget? child) => + AttendanceProvider value, + Widget? child) => CommonElevatedButton( backgroundColor: const Color(0xff004791), borderRadius: 30, @@ -157,27 +160,25 @@ class _MarkAttendanceScreenState extends State { onPressed: () { value .markAttendance( - dateController.text.trim(), - timeController.text.trim(), - locationController.text.trim(), - notesController.text.trim()) + dateController.text.trim(), // Pass date + timeController.text.trim(), // Pass time + locationController.text.trim(), // Pass location + notesController.text.trim()) // Pass notes .then( - (result) { - var (status, message) = result; + (result) { + var (status, message) = result; // Destructure the result ScaffoldMessenger.of(context) .showSnackBar(SnackBar( - content: Text(message))); + content: Text(message))); // Show a message based on the result if (status) { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => - const AttendanceSuccess())); + const AttendanceSuccess())); // Navigate to success screen on success } }, ); - - // Navigator.push(context, MaterialPageRoute(builder:(context) => const AttendanceSuccess(),)); }), ), const SizedBox(height: 15), @@ -192,7 +193,7 @@ class _MarkAttendanceScreenState extends State { context, MaterialPageRoute( builder: (context) => - const OnLeaveScreen(), + const OnLeaveScreen(), // Navigate to leave screen )); }) ], @@ -211,16 +212,14 @@ class _MarkAttendanceScreenState extends State { right: 0, bottom: 0, child: Consumer(builder: (context, value, child) { - if(value.isLoading){ - return - Container( - color: Colors.black12, - child: const Center(child: CircularProgressIndicator())); + if (value.isLoading) { // Show loading indicator if loading + return Container( + color: Colors.black12, + child: const Center(child: CircularProgressIndicator())); + } else { + return const SizedBox(); // Return empty widget if not loading } - else{ - return const SizedBox(); - } - }, ), + }), ) ], ), diff --git a/lib/screens/notification_screen.dart b/lib/screens/notification_screen.dart index 9503bca..bdacede 100644 --- a/lib/screens/notification_screen.dart +++ b/lib/screens/notification_screen.dart @@ -8,6 +8,7 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +// Main screen that displays notifications class NotificationScreen extends StatefulWidget { const NotificationScreen({super.key}); @@ -20,12 +21,14 @@ class NotificationScreenState extends State { @override void initState() { + // Initialize the NotificationProvider in the state when the screen is created _notificationProvider = NotificationProvider(); super.initState(); } @override Widget build(BuildContext context) { + // Use ChangeNotifierProvider to supply NotificationProvider to the widget tree return ChangeNotifierProvider( create: (context) => _notificationProvider, child: CommonBackground( @@ -35,6 +38,7 @@ class NotificationScreenState extends State { actions: [ IconButton( onPressed: () { + // Pop the screen when the back button is pressed Navigator.pop(context); }, icon: Image.asset('assets/Back_attendance.png'), @@ -51,9 +55,12 @@ class NotificationScreenState extends State { elevation: 0, ), drawer: const CommonDrawer(), + // Consumer listens for changes in the NotificationProvider body: Consumer( builder: (context, value, child) => value.isLoading + // Display a loading indicator if notifications are still loading ? const Center(child: CircularProgressIndicator()) + // Show the list of notifications once loaded : MyListView(value: value), ), ), @@ -62,6 +69,7 @@ class NotificationScreenState extends State { } } +// Function that builds a button for a product Widget buildProductButton(String productName) { return Padding( padding: const EdgeInsets.only(bottom: 15), @@ -72,12 +80,14 @@ Widget buildProductButton(String productName) { text: productName, backgroundColor: const Color(0xff004791), onPressed: () { + // Log when the button is pressed debugPrint('$productName pressed'); }, ), ); } +// Widget that displays a list of notifications grouped by date class MyListView extends StatelessWidget { final NotificationProvider value; @@ -85,8 +95,10 @@ class MyListView extends StatelessWidget { @override Widget build(BuildContext context) { + // Group notifications by their creation date Map> groupedNotifications = {}; + // Iterate over the notification list and group by formatted date for (var notification in value.notificationList) { String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt ?? '')); if (!groupedNotifications.containsKey(date)) { @@ -95,9 +107,10 @@ class MyListView extends StatelessWidget { groupedNotifications[date]!.add(notification); } + // Build a ListView for grouped notifications return ListView.builder( padding: const EdgeInsets.only(top: 15), - itemCount: groupedNotifications.length, + itemCount: groupedNotifications.length, // Number of date groups itemBuilder: (context, index) { String date = groupedNotifications.keys.elementAt(index); List notificationsForDate = groupedNotifications[date]!; @@ -107,7 +120,7 @@ class MyListView extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Display the date once + // Display the date for each group of notifications Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Text( @@ -115,12 +128,13 @@ class MyListView extends StatelessWidget { style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), + // Display each notification in an expandable tile ...notificationsForDate.map((item) => Padding( padding: const EdgeInsets.only(bottom: 10), child: ExpansionTile( collapsedBackgroundColor: Colors.white, backgroundColor: Colors.white, - trailing: const SizedBox.shrink(), + trailing: const SizedBox.shrink(), // Remove trailing icon title: Text( item.title ?? '', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), diff --git a/lib/screens/on_leave_screen.dart b/lib/screens/on_leave_screen.dart index 57bff94..f0f4cb2 100644 --- a/lib/screens/on_leave_screen.dart +++ b/lib/screens/on_leave_screen.dart @@ -11,6 +11,7 @@ import '../widgets/common_app_bar.dart'; import '../widgets/common_elevated_button.dart'; import '../widgets/common_text_form_field.dart'; +// Screen for marking leave class OnLeaveScreen extends StatefulWidget { const OnLeaveScreen({super.key}); @@ -21,23 +22,26 @@ class OnLeaveScreen extends StatefulWidget { class _OnLeaveScreenState extends State { late MarkLeaveProvider _markLeaveProvider; + // Controllers for input fields final dateController = TextEditingController( text: DateFormat('yyyy/MM/dd').format(DateTime.now())); final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); final locationController = TextEditingController(); final notesController = TextEditingController(); - String selectedLeaveType = 'Sick'; + String selectedLeaveType = 'Sick'; // Default selected leave type + // Callback to update the selected leave type void onLeaveTypeSelected(String leaveType) { setState(() { selectedLeaveType = leaveType; }); } + // Widget to build an option for leave type selection Widget buildLeaveTypeOption(String leaveType) { bool isSelected = leaveType == selectedLeaveType; @@ -67,17 +71,18 @@ class _OnLeaveScreenState extends State { @override void initState() { super.initState(); - _markLeaveProvider = MarkLeaveProvider(); - _getCurrentLocation(); + _markLeaveProvider = MarkLeaveProvider(); // Initialize the leave provider + _getCurrentLocation(); // Fetch the current location when the screen is initialized } + // Method to get the current location Future _getCurrentLocation() async { LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { debugPrint('location denied'); - return; + return; // Exit if permission is denied } } @@ -85,21 +90,24 @@ class _OnLeaveScreenState extends State { setState(() { debugPrint('Location permissions are permanently denied'); }); - return; + return; // Exit if permission is permanently denied } + // Get the current position with high accuracy Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); debugPrint('position--- $position'); + // Convert position coordinates to a human-readable address List placeMarks = - await placemarkFromCoordinates(position.latitude, position.longitude) - .timeout(const Duration(seconds: 10)); + await placemarkFromCoordinates(position.latitude, position.longitude) + .timeout(const Duration(seconds: 10)); debugPrint('place mark--- $placeMarks'); Placemark place = placeMarks[0]; setState(() { + // Set the location in the text field locationController.text = '${place.subLocality}, ${place.locality}'; }); } @@ -109,137 +117,142 @@ class _OnLeaveScreenState extends State { return ChangeNotifierProvider( create: (_) => _markLeaveProvider, builder: (context, 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: const Text('On Leave', - style: TextStyle( - fontSize: 28, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - backgroundColor: Colors.transparent, - elevation: 0, + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + // Navigate back when the back button is pressed + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), ), - drawer: const CommonDrawer(), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 16), - Container( - padding: const EdgeInsets.all(20.0) - .copyWith(top: 30, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 30.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CommonTextFormField( - title: 'Date', - readOnly: true, - fillColor: Colors.white, - controller: dateController), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Time', - readOnly: true, - fillColor: Colors.white, - controller: timeController), - const SizedBox(height: 15), - const Text('Leave type', - style: TextStyle( - fontSize: 15, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - const SizedBox(height: 3), - Wrap( - direction: Axis.horizontal, - children: [ - buildLeaveTypeOption('Sick'), - const SizedBox(width: 10), - buildLeaveTypeOption('Personal'), - const SizedBox(width: 10), - buildLeaveTypeOption('Privilege '), - ], - ), - const SizedBox(height: 15), - CommonTextFormField( - height: 100, - title: 'Reason', - fillColor: Colors.white, - maxLines: 4, - controller: notesController), - const SizedBox(height: 16), - Align( - alignment: Alignment.center, - child: Consumer( - builder: (context, value, child) => - CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'ON LEAVE', - backgroundColor: - const Color(0xff00784C), - onPressed: () { - value - .markLeave( - dateController.text - .trim(), - timeController.text - .trim(), - locationController.text - .trim(), - notesController.text - .trim(), - selectedLeaveType) - .then( - (result) { - var (status, message) = - result; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar( - content: - Text(message))); - if (status) { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => - const AttendanceSuccess())); - } - }, - ); - - // Navigator.push(context, MaterialPageRoute(builder:(context) => const AttendanceSuccess(),)); - }), - )), + ], + title: const Text('On Leave', + style: TextStyle( + fontSize: 28, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, + ), + drawer: const CommonDrawer(), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(20.0) + .copyWith(top: 30, bottom: 30), + margin: const EdgeInsets.symmetric(horizontal: 30.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.9), + borderRadius: BorderRadius.circular(26.0)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Text field for date + CommonTextFormField( + title: 'Date', + readOnly: true, + fillColor: Colors.white, + controller: dateController), + const SizedBox(height: 15), + // Text field for time + CommonTextFormField( + title: 'Time', + readOnly: true, + fillColor: Colors.white, + controller: timeController), + const SizedBox(height: 15), + const Text('Leave type', + style: TextStyle( + fontSize: 15, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + const SizedBox(height: 3), + // Options for leave types + Wrap( + direction: Axis.horizontal, + children: [ + buildLeaveTypeOption('Sick'), + const SizedBox(width: 10), + buildLeaveTypeOption('Personal'), + const SizedBox(width: 10), + buildLeaveTypeOption('Privilege '), ], ), - ), - ], + const SizedBox(height: 15), + // Text area for reason + CommonTextFormField( + height: 100, + title: 'Reason', + fillColor: Colors.white, + maxLines: 4, + controller: notesController), + const SizedBox(height: 16), + Align( + alignment: Alignment.center, + child: Consumer( + builder: (context, value, child) => + CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'ON LEAVE', + backgroundColor: + const Color(0xff00784C), + onPressed: () { + // Call the markLeave method when the button is pressed + value + .markLeave( + dateController.text + .trim(), + timeController.text + .trim(), + locationController.text + .trim(), + notesController.text + .trim(), + selectedLeaveType) + .then( + (result) { + var (status, message) = + result; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: + Text(message))); + if (status) { + // Navigate to success screen if leave is marked successfully + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const AttendanceSuccess())); + } + }, + ); + }), + )), + ], + ), ), - ), + ], ), ), - )); + ), + ), + )); } } diff --git a/lib/screens/password_change_screen.dart b/lib/screens/password_change_screen.dart index b70079c..c60c088 100644 --- a/lib/screens/password_change_screen.dart +++ b/lib/screens/password_change_screen.dart @@ -2,6 +2,7 @@ import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:flutter/material.dart'; +// Screen to display after password change class PasswordChangeScreen extends StatefulWidget { const PasswordChangeScreen({super.key}); @@ -13,6 +14,7 @@ class PasswordChangeScreenState extends State { @override void initState() { super.initState(); + // Navigate to the HomePage after a delay of 2 seconds Future.delayed(const Duration(seconds: 2), () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage())); @@ -24,7 +26,7 @@ class PasswordChangeScreenState extends State { return Scaffold( appBar: AppBar( leading: InkWell( - onTap: () => Navigator.pop(context), + onTap: () => Navigator.pop(context), // Go back to the previous screen child: Image.asset('assets/Back_attendance.png')), backgroundColor: Colors.transparent), body: CommonBackground( diff --git a/lib/screens/product_purchase_data.dart b/lib/screens/product_purchase_data.dart index 68a2fad..c9a5951 100644 --- a/lib/screens/product_purchase_data.dart +++ b/lib/screens/product_purchase_data.dart @@ -7,6 +7,7 @@ import '../widgets/common_app_bar.dart'; import '../widgets/common_elevated_button.dart'; import '../widgets/common_text_form_field.dart'; +// Screen for entering product purchase data class ProductPurchaseData extends StatefulWidget { const ProductPurchaseData({super.key}); @@ -16,11 +17,12 @@ class ProductPurchaseData extends StatefulWidget { class ProductPurchaseDataState extends State { final dateController = TextEditingController( - text: DateFormat('dd/MM/yyyy').format(DateTime.now())); + text: DateFormat('dd/MM/yyyy').format(DateTime.now())); // Initialize date controller with current date final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); // Initialize time controller with current time + // Controllers for other input fields final locationController = TextEditingController(); final notesController = TextEditingController(); final dealercontroller = TextEditingController(); @@ -29,19 +31,19 @@ class ProductPurchaseDataState extends State { final salesController = TextEditingController(); final commentController = TextEditingController(); - @override Widget build(BuildContext context) { return CommonBackground( - child: Scaffold(backgroundColor: Colors.transparent, + child: Scaffold( + backgroundColor: Colors.transparent, appBar: CommonAppBar( actions: [ IconButton( - onPressed: () - { - Navigator.pop(context); + onPressed: () { + Navigator.pop(context); // Navigate back to the previous screen }, - icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), ), ], title: const Text('Product Purchase Data', @@ -49,7 +51,9 @@ class ProductPurchaseDataState extends State { fontSize: 20, color: Colors.black, fontWeight: FontWeight.w400, - fontFamily: 'Anek')), backgroundColor: Colors.transparent, elevation: 0, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, ), drawer: const CommonDrawer(), body: Padding( @@ -64,7 +68,6 @@ class ProductPurchaseDataState extends State { Container( padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), - // margin: const EdgeInsets.symmetric(horizontal: 30.0), decoration: BoxDecoration( border: Border.all(color: Colors.white), color: const Color(0xffB4D1E5).withOpacity(0.9), @@ -72,18 +75,21 @@ class ProductPurchaseDataState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Input field for selecting a dealer CommonTextFormField( title: 'Select Dealer: Dealer 1', fillColor: Colors.white, controller: dealercontroller), const SizedBox(height: 15), + // Input field for selecting a product CommonTextFormField( title: 'Select Product: Product A', fillColor: Colors.white, controller: productController), const SizedBox(height: 15), + // Read-only input field for date range CommonTextFormField( title: 'Date Range: 10-06 to 20-06', readOnly: true, @@ -91,26 +97,31 @@ class ProductPurchaseDataState extends State { controller: dateController), const SizedBox(height: 15), + // Button to view data, navigates to the success screen on press Align( alignment: Alignment.center, - child: CommonElevatedButton( + child: CommonElevatedButton( borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, text: 'VIEW DATA', backgroundColor: const Color(0xff004791), - onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen(),)), - - ) - + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const DataSubmitSuccessFullScreen(), + ), + ), ), - ], + ), + ], ), ), ], ), ), - ),), + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/products_manual_screen.dart b/lib/screens/products_manual_screen.dart index ecb3530..e393e5d 100644 --- a/lib/screens/products_manual_screen.dart +++ b/lib/screens/products_manual_screen.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import '../models/product_manual_model.dart'; import '../provider/product_manual_provider.dart'; +// Screen to display product manuals class ProductsManualScreen extends StatefulWidget { const ProductsManualScreen({super.key}); @@ -21,7 +22,7 @@ class _ProductsManualScreenState extends State { @override void initState() { - _productManualProvider = ProductManualProvider(); + _productManualProvider = ProductManualProvider(); // Initialize the product manual provider super.initState(); } @@ -36,7 +37,7 @@ class _ProductsManualScreenState extends State { actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Navigate back to the previous screen }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), @@ -67,13 +68,13 @@ class _ProductsManualScreenState extends State { builder: (context, provider, child) { if (provider.isLoading) { return const Center( - child: CircularProgressIndicator(), + child: CircularProgressIndicator(), // Show loading indicator while data is being fetched ); } if (provider.productManualList.isEmpty) { return const Center( - child: Text('No products available.')); + child: Text('No products available.')); // Message when no products are available } return Container( @@ -89,8 +90,8 @@ class _ProductsManualScreenState extends State { children: provider.productManualList .map( (product) => - _buildProductButton(context, product), - ) + _buildProductButton(context, product), // Build buttons for each product + ) .toList(), ), ); @@ -105,6 +106,7 @@ class _ProductsManualScreenState extends State { ); } + // Method to build a button for each product Widget _buildProductButton(BuildContext context, ProductManualModel product) { return Padding( padding: const EdgeInsets.only(bottom: 15), @@ -112,12 +114,12 @@ class _ProductsManualScreenState extends State { borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, - text: product.title, + text: product.title, // Display product title on the button backgroundColor: const Color(0xff004791), onPressed: () => Navigator.push( context, MaterialPageRoute( - builder: (context) => ViewPdfScreen(productManualModel: product), + builder: (context) => ViewPdfScreen(productManualModel: product), // Navigate to the PDF view screen ), ), ), diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index 4b9df96..407445e 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -4,6 +4,8 @@ import 'package:provider/provider.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_drawer.dart'; + +// Profile screen to display user information class ProfileScreen extends StatelessWidget { const ProfileScreen({Key? key}) : super(key: key); @@ -14,16 +16,16 @@ class ProfileScreen extends StatelessWidget { CommonBackground( isFullWidth: true, child: Scaffold( - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Navigation drawer for the app backgroundColor: Colors.transparent, appBar: CommonAppBar( - title: const Text('Profile'), + title: const Text('Profile'), // Title of the app bar backgroundColor: Colors.transparent, elevation: 0, actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Navigate back to the previous screen }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), @@ -32,33 +34,33 @@ class ProfileScreen extends StatelessWidget { ), body: Consumer( builder: (context, profileProvider, child) { - final profileData = profileProvider.profileResponse?.myData!; - return SingleChildScrollView( - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 20), - _buildProfileItem('Name', profileData?.name ?? ''), - _buildProfileItem('ID', profileData?.uniqueId ?? ''), - _buildProfileItem('Email ID', profileData?.email ?? ''), - _buildProfileItem('Mobile Number', profileData?.mobileNumber ?? ''), - _buildProfileItem('Designation', profileData?.designation ?? ''), - ], - ), + final profileData = profileProvider.profileResponse?.myData!; // Fetch user profile data + return SingleChildScrollView( + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), + margin: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.9), + borderRadius: BorderRadius.circular(26.0), ), - ], - ), - ); + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 20), // Spacing for aesthetics + _buildProfileItem('Name', profileData?.name ?? ''), // Display Name + _buildProfileItem('ID', profileData?.uniqueId ?? ''), // Display User ID + _buildProfileItem('Email ID', profileData?.email ?? ''), // Display Email + _buildProfileItem('Mobile Number', profileData?.mobileNumber ?? ''), // Display Mobile Number + _buildProfileItem('Designation', profileData?.designation ?? ''), // Display Designation + ], + ), + ), + ], + ), + ); }, ), ), @@ -67,6 +69,7 @@ class ProfileScreen extends StatelessWidget { ); } + // Method to create a profile item with a label and value Widget _buildProfileItem(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), @@ -81,7 +84,7 @@ class ProfileScreen extends StatelessWidget { color: Color(0xff004791), ), ), - const SizedBox(height: 5), + const SizedBox(height: 5), // Space between label and value Text( value, style: const TextStyle( @@ -89,9 +92,9 @@ class ProfileScreen extends StatelessWidget { color: Colors.black, ), ), - const Divider(color: Colors.grey), + const Divider(color: Colors.grey), // Divider for visual separation ], ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/rejected_application_screen.dart b/lib/screens/rejected_application_screen.dart index 43ecfbf..e6a3674 100644 --- a/lib/screens/rejected_application_screen.dart +++ b/lib/screens/rejected_application_screen.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import '../provider/rejected_provider.dart'; +// Main screen for displaying rejected applications class RejectedApplicationScreen extends StatefulWidget { const RejectedApplicationScreen({super.key}); @@ -22,14 +23,14 @@ class _RejectedApplicationScreenState extends State { @override void initState() { - _rejectedProvider = RejectedProvider(); + _rejectedProvider = RejectedProvider(); // Initialize the provider for rejected applications super.initState(); } @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => _rejectedProvider, + create: (context) => _rejectedProvider, // Provide the rejected provider to the widget tree child: CommonBackground( child: Scaffold( backgroundColor: Colors.transparent, @@ -37,7 +38,7 @@ class _RejectedApplicationScreenState extends State { actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Navigate back to the previous screen }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), @@ -48,21 +49,22 @@ class _RejectedApplicationScreenState extends State { fontSize: 20, color: Colors.black, fontWeight: FontWeight.w400, - fontFamily: 'Anek')), + fontFamily: 'Anek')), // Title of the app bar backgroundColor: Colors.transparent, elevation: 0, ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Navigation drawer body: Consumer( builder: (context, value, child) => value.isLoading - ? const Center(child: CircularProgressIndicator()) - : MyListView(value: value), + ? const Center(child: CircularProgressIndicator()) // Loading indicator while data is being fetched + : MyListView(value: value), // Display the list of rejected applications ), ), )); } } +// Button to display product information Widget buildProductButton(String productName) { return Padding( padding: const EdgeInsets.only(bottom: 15), @@ -80,6 +82,7 @@ Widget buildProductButton(String productName) { ); } +// List view for displaying rejected application details class MyListView extends StatelessWidget { final RejectedProvider value; @@ -89,7 +92,7 @@ class MyListView extends StatelessWidget { Widget build(BuildContext context) { return ListView.builder( padding: const EdgeInsets.only(top: 15), - itemCount: value.rejectedApplicationList.length, + itemCount: value.rejectedApplicationList.length, // Number of rejected applications itemBuilder: (context, index) { RejectedApplicationResponse item = value.rejectedApplicationList[index]; return Padding( @@ -98,75 +101,73 @@ class MyListView extends StatelessWidget { collapsedBackgroundColor: Colors.white, backgroundColor: Colors.white, title: Text( - item.tradeName ?? '', + item.tradeName ?? '', // Trade name of the application 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 ?? ''), + .format(DateTime.parse(item.createdAt ?? '')))), // Formatted date of application + Text(item.sId ?? ''), // Unique application ID + for (var note in item.notes!) Text(note.message ?? ''), // Display application notes ], ), children: [ ListTile( - title: Text('Address: ${item.address ?? ''}'), + title: Text('Address: ${item.address ?? ''}'), // Application address ), ListTile( - title: Text('City: ${item.city ?? ''}'), + title: Text('City: ${item.city ?? ''}'), // Application city ), ListTile( - title: Text('State: ${item.state ?? ''}'), + title: Text('State: ${item.state ?? ''}'), // Application state ), ListTile( - title: Text('Pincode: ${item.pincode ?? ''}'), + title: Text('Pincode: ${item.pincode ?? ''}'), // Application pincode ), ListTile( - title: Text('Mobile: ${item.mobileNumber ?? ''}'), + title: Text('Mobile: ${item.mobileNumber ?? ''}'), // Mobile number associated with application ), ListTile( - title: Text('Status: ${item.status ?? ''}'), + title: Text('Status: ${item.status ?? ''}'), // Current status of the application ), ListTile( title: Text( - 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), + 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), // Principal distributor's name ), ListTile( - title: Text('PAN Number: ${item.panNumber ?? ''}'), + title: Text('PAN Number: ${item.panNumber ?? ''}'), // PAN number associated with application ), - Image.network(item.panImg!.url ?? '', height: 250, width: 250), + Image.network(item.panImg!.url ?? '', height: 250, width: 250), // PAN image ListTile( - title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'), + title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'), // Aadhar number associated with application ), - Image.network(item.aadharImg?.url ?? '', height: 250, width: 250), + Image.network(item.aadharImg?.url ?? '', height: 250, width: 250), // Aadhar image ListTile( - title: Text('GST Number: ${item.gstNumber ?? ''}'), + title: Text('GST Number: ${item.gstNumber ?? ''}'), // GST number associated with application ), - Image.network(item.gstImg!.url ?? '', height: 250, width: 250), + Image.network(item.gstImg!.url ?? '', height: 250, width: 250), // GST image const ListTile( - title: Text('Pesticide License: '), + title: Text('Pesticide License: '), // Pesticide license information ), 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'] ?? ''), + height: 250, width: 250), // Pesticide license image const ListTile( - title: Text('selfieEntranceImg: '), + title: Text('selfieEntranceImg: '), // Selfie entrance image information ), Image.network(item.selfieEntranceImg!.url ?? '', - height: 250, width: 250), + height: 250, width: 250), // Selfie entrance image const ListTile( - title: Text('Notes:'), + title: Text('Notes:'), // Display notes for the application ), if (item.notes != null) - for (var note in item.notes!) + for (var note in item.notes!) // Iterate over notes and display each ListTile( contentPadding: const EdgeInsets.only(left: 20), - title: Text(note.message ?? ''), + title: Text(note.message ?? ''), // Note message ), ], ), diff --git a/lib/screens/retailer_details_screen.dart b/lib/screens/retailer_details_screen.dart index 72a54c1..b2ebd2e 100644 --- a/lib/screens/retailer_details_screen.dart +++ b/lib/screens/retailer_details_screen.dart @@ -31,175 +31,186 @@ class RetailerDetailsScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 16), + // Container for the retailer details form Container( - padding: const EdgeInsets.all(20.0) - .copyWith(top: 30, bottom: 30), + padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), decoration: BoxDecoration( border: Border.all(color: Colors.white), color: const Color(0xffB4D1E5).withOpacity(0.9), borderRadius: BorderRadius.circular(26.0)), child: Form( - key: value.retailerDetailsFormKey, + key: value.retailerDetailsFormKey, // Key to manage form state child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( children: [ const SizedBox(height: 15), + // Text field for Trade Name CommonTextFormField( title: 'Trade Name', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'Trade Name cannot be empty'; + return 'Trade Name cannot be empty'; // Validation message } return null; }, controller: value.tradeNameController), const SizedBox(height: 15), + // Text field for Name CommonTextFormField( title: 'Name', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'Name cannot be empty'; + return 'Name cannot be empty'; // Validation message } return null; }, controller: value.nameController), const SizedBox(height: 15), + // Text field for Address CommonTextFormField( title: 'Address', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'Address cannot be empty'; + return 'Address cannot be empty'; // Validation message } return null; }, controller: value.addressController), const SizedBox(height: 15), + // Country, State, and City Picker CSCPicker( defaultCountry: CscCountry.India, disableCountry: true, onCountryChanged: (val) { setState(() { - value.country.text = val; + value.country.text = val; // Update country }); }, onStateChanged: (val) { setState(() { - value.state.text = val ?? ''; + value.state.text = val ?? ''; // Update state }); }, onCityChanged: (val) { setState(() { - value.city.text = val ?? ''; + value.city.text = val ?? ''; // Update city }); }, ), const SizedBox(height: 15), + // Text field for District CommonTextFormField( title: 'District', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'District cannot be empty'; + return 'District cannot be empty'; // Validation message } return null; }, controller: value.districtController), const SizedBox(height: 15), + // Text field for Pincode CommonTextFormField( maxLength: 6, title: 'Pincode', fillColor: Colors.white, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly // Only digits allowed ], validator: (String? value) { if (value!.isEmpty) { - return 'Pincode cannot be empty'; + return 'Pincode cannot be empty'; // Validation message } return null; }, controller: value.pinCodeController), const SizedBox(height: 15), + // Text field for Mobile Number CommonTextFormField( maxLength: 10, title: 'Mobile Number', fillColor: Colors.white, inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly // Only digits allowed ], validator: (String? value) { if (value!.isEmpty) { - return 'Mobile Number cannot be empty'; + return 'Mobile Number cannot be empty'; // Validation message } return null; }, controller: value.mobileNumberController), const SizedBox(height: 15), + // Text field for Email CommonTextFormField( - // maxLength: 20, title: 'Email', fillColor: Colors.white, inputFormatters: [ - NoSpaceFormatter(), + NoSpaceFormatter(), // No spaces allowed ], validator: (String? value) { if (value!.isEmpty) { - return 'Email cannot be empty'; + return 'Email cannot be empty'; // Validation message } return null; }, controller: value.emailController), const SizedBox(height: 15), + // Text field for Aadhar Number CommonTextFormField( maxLength: 12, title: 'Aadhar Number', inputFormatters: [ - FilteringTextInputFormatter.digitsOnly + FilteringTextInputFormatter.digitsOnly // Only digits allowed ], fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'Aadhar Number cannot be empty'; + return 'Aadhar Number cannot be empty'; // Validation message } return null; }, controller: value.aadharNumberController), const SizedBox(height: 15), + // Text field for PAN Number CommonTextFormField( inputFormatters: [ - UpperCaseTextFormatter(), + UpperCaseTextFormatter(), // Convert to uppercase ], maxLength: 10, title: 'PAN Number', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'PAN Number cannot be empty'; + return 'PAN Number cannot be empty'; // Validation message } return null; }, controller: value.panNumberController), const SizedBox(height: 15), + // Text field for GST Number CommonTextFormField( inputFormatters: [ - UpperCaseTextFormatter(), + UpperCaseTextFormatter(), // Convert to uppercase ], maxLength: 15, title: 'GST Number', fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { - return 'GST Number cannot be empty'; + return 'GST Number cannot be empty'; // Validation message } return null; }, controller: value.gstNumberController), const SizedBox(height: 15), + // Dropdown for selecting Mapped Principal Distributor DropdownButtonFormField( decoration: InputDecoration( fillColor: Colors.white, @@ -215,17 +226,18 @@ class RetailerDetailsScreenState extends State { }).toList(), onChanged: (String? newValue) { setState(() { - value.selectedDistributor = newValue; + value.selectedDistributor = newValue; // Update selected distributor }); }, validator: (String? value) { if (value == null || value.isEmpty) { - return 'Mapped Principal Distributor cannot be empty'; + return 'Mapped Principal Distributor cannot be empty'; // Validation message } return null; }, ), const SizedBox(height: 30), + // Continue button to navigate to the next step Align( alignment: Alignment.center, child: CommonElevatedButton( @@ -236,8 +248,8 @@ class RetailerDetailsScreenState extends State { backgroundColor: const Color(0xff004791), onPressed: () { if (value.retailerDetailsFormKey.currentState! - .validate()) { - value.tabController.animateTo(1); + .validate()) { // Validate form + value.tabController.animateTo(1); // Navigate to next tab } }, ), diff --git a/lib/screens/sales_task_screen.dart b/lib/screens/sales_task_screen.dart index 92d351e..325778f 100644 --- a/lib/screens/sales_task_screen.dart +++ b/lib/screens/sales_task_screen.dart @@ -22,10 +22,11 @@ class _SalesTaskScreenState extends State { @override void initState() { super.initState(); - _dailyTaskProvider = DailyTaskProvider(); - apiCall(); + _dailyTaskProvider = DailyTaskProvider(); // Initialize the provider + apiCall(); // Fetch tasks when the screen initializes } + // Function to call API for fetching tasks void apiCall() async { await _dailyTaskProvider.getTask(type: 'New', isAddPending: true); await _dailyTaskProvider.getTask(type: 'Pending', isAddPending: true); @@ -34,27 +35,28 @@ class _SalesTaskScreenState extends State { @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => _dailyTaskProvider, + create: (context) => _dailyTaskProvider, // Provide the daily task provider child: Scaffold( extendBodyBehindAppBar: true, - appBar: _buildAppBar(), - drawer: const CommonDrawer(), + appBar: _buildAppBar(), // Build the app bar + drawer: const CommonDrawer(), // Add a common drawer body: CommonBackground( child: SafeArea( - child: _buildTaskList(), + child: _buildTaskList(), // Build the task list ), ), ), ); } + // Method to build the app bar CommonAppBar _buildAppBar() { return CommonAppBar( backgroundColor: Colors.transparent, elevation: 0, actions: [ IconButton( - onPressed: () => Navigator.pop(context), + onPressed: () => Navigator.pop(context), // Back button functionality icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), ), @@ -66,18 +68,21 @@ class _SalesTaskScreenState extends State { ); } + // Method to build the task list Widget _buildTaskList() { return Consumer( builder: (context, value, child) { if (value.isLoading) { - return const Center(child: CircularProgressIndicator()); + return const Center(child: CircularProgressIndicator()); // Loading indicator } + // Filter sales tasks for "Update Sales Data" final salesTasks = value.newTasksList .where((task) => - task.task?.toLowerCase() == 'Update Sales Data'.toLowerCase()) + task.task?.toLowerCase() == 'Update Sales Data'.toLowerCase()) .toList(); + // If no tasks are found, display a message if (salesTasks.isEmpty) { return const Center( child: Text( @@ -91,19 +96,22 @@ class _SalesTaskScreenState extends State { ); } + // Build the list of tasks return ListView.separated( padding: const EdgeInsets.all(16), itemCount: salesTasks.length, separatorBuilder: (context, index) => const SizedBox(height: 8), - itemBuilder: (context, index) => _buildTaskCard(salesTasks[index]), + itemBuilder: (context, index) => _buildTaskCard(salesTasks[index]), // Build each task card ); }, ); } + // Method to build individual task cards Widget _buildTaskCard(Tasks tasksList) { return InkWell( onTap: () { + // Navigate to AddSalesProductScreen with the selected task details if (tasksList.sId != null && tasksList.addedFor != null) { Navigator.push( navigatorKey.currentContext!, @@ -119,9 +127,9 @@ class _SalesTaskScreenState extends State { color: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), child: ListTile( - leading: const Icon(Icons.attach_money, color: Colors.greenAccent), + leading: const Icon(Icons.attach_money, color: Colors.greenAccent), // Icon for the task title: Text( - tasksList.task ?? '', + tasksList.task ?? '', // Task name style: const TextStyle( color: Colors.black87, fontWeight: FontWeight.w700, @@ -132,15 +140,15 @@ class _SalesTaskScreenState extends State { subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Distributor: ${tasksList.addedFor ?? ""}'), - Text('Trader Name: ${tasksList.tradeName ?? ''}'), + Text('Distributor: ${tasksList.addedFor ?? ""}'), // Distributor information + Text('Trader Name: ${tasksList.tradeName ?? ''}'), // Trader name if (tasksList.taskDueDate != null) Text( - 'Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), - Text('Priority: ${tasksList.taskPriority}'), + 'Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), // Formatted due date + Text('Priority: ${tasksList.taskPriority}'), // Task priority ], ), - trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), // Arrow icon for navigation ), ), ); diff --git a/lib/screens/select_taskkyc_screen.dart b/lib/screens/select_taskkyc_screen.dart index 1223af3..5d896db 100644 --- a/lib/screens/select_taskkyc_screen.dart +++ b/lib/screens/select_taskkyc_screen.dart @@ -20,48 +20,49 @@ class SelectTaskkycScreenState extends State { @override void initState() { - _selectTaskProvider = SelectTaskProvider(); + _selectTaskProvider = SelectTaskProvider(); // Initialize the SelectTaskProvider super.initState(); } @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => _selectTaskProvider, + create: (context) => _selectTaskProvider, // Provide the SelectTaskProvider child: CommonBackground( child: Scaffold( backgroundColor: Colors.transparent, appBar: CommonAppBar( - title: const Text('Select Task'), + title: const Text('Select Task'), // Title for the app bar backgroundColor: Colors.transparent, elevation: 0, actions: [ IconButton( - onPressed: () => Navigator.pop(context), + onPressed: () => Navigator.pop(context), // Back button functionality icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), ), ], ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Common navigation drawer body: Consumer( builder: (context, value, child) => value.isLoading - ? const Center(child: CircularProgressIndicator()) - : _buildTaskList(), + ? const Center(child: CircularProgressIndicator()) // Loading indicator while tasks are fetched + : _buildTaskList(), // Build the task list once loading is complete ), ), ), ); } + // Method to build the list of tasks Widget _buildTaskList() { return Consumer( builder: (context, value, child) { if (value.tasksList.isEmpty) { return const Center( child: Text( - 'NO TASK', + 'NO TASK', // Message displayed if no tasks are available style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -71,32 +72,33 @@ class SelectTaskkycScreenState extends State { ); } return ListView.builder( - itemCount: value.tasksList.length, - itemBuilder: (context, index) => _buildTaskCard(value.tasksList[index]), + itemCount: value.tasksList.length, // Number of tasks to display + itemBuilder: (context, index) => _buildTaskCard(value.tasksList[index]), // Build each task card ); }, ); } + // Method to build an individual task card Widget _buildTaskCard(Tasks task) { return InkWell( onTap: () => Navigator.push( context, MaterialPageRoute( - builder: (context) => CollectKycScreen(id: task.sId ?? ''), + builder: (context) => CollectKycScreen(id: task.sId ?? ''), // Navigate to CollectKycScreen ), ), child: Card( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), // Margin around the card color: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( - leading: const Icon(Icons.task, color: Colors.blueAccent), + leading: const Icon(Icons.task, color: Colors.blueAccent), // Task icon title: Text( - task.task ?? '', + task.task ?? '', // Task title style: const TextStyle( color: Colors.black87, fontWeight: FontWeight.w700, @@ -104,19 +106,19 @@ class SelectTaskkycScreenState extends State { fontFamily: 'Anek', ), ), - trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87), // Arrow icon for navigation ), Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Note: ${task.note}'), + Text('Note: ${task.note}'), // Display task note if (task.taskDueDate != null) Text( - 'Date: ${task.taskDueDate == null ? '' : DateFormat('dd/MM/yyyy').format(DateTime.parse(task.taskDueDate ?? ''))}', + 'Date: ${task.taskDueDate == null ? '' : DateFormat('dd/MM/yyyy').format(DateTime.parse(task.taskDueDate ?? ''))}', // Formatted due date ), - Text('Priority: ${task.taskPriority}'), + Text('Priority: ${task.taskPriority}'), // Display task priority ], ), ), @@ -127,17 +129,19 @@ class SelectTaskkycScreenState extends State { } } +// Base class for task items class TaskItem { - final String title; - final Widget screen; + final String title; // Title of the task + final Widget screen; // Screen to navigate to TaskItem({required this.title, required this.screen}); } +// Extended class for KYC task items with additional attributes class KycTaskItem extends TaskItem { - final String note; - final String date; - final String priority; + final String note; // Note associated with the task + final String date; // Due date of the task + final String priority; // Priority of the task KycTaskItem({ required String title, @@ -146,4 +150,4 @@ class KycTaskItem extends TaskItem { required this.date, required this.priority, }) : super(title: title, screen: screen); -} \ No newline at end of file +} diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index 4e9f801..77ddee2 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -16,59 +16,75 @@ class _SplashScreenState extends State { @override void initState() { super.initState(); + // Delay for 3 seconds to show the splash screen before checking user authentication Future.delayed(const Duration(seconds: 3), () { - SecureStorageService().read( - key: 'access_token').then((value){ - if(value != null){ - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => const HomePage())); - }else{ - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => const LoginPage())); - } - }); + // Read the access token from secure storage + SecureStorageService().read(key: 'access_token').then((value) { + // Check if the access token exists + if (value != null) { + // If the token exists, navigate to the HomePage + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => const HomePage())); + } else { + // If the token doesn't exist, navigate to the LoginPage + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => const LoginPage())); + } + }); }); } @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, - body: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.only(top: 110, bottom: 50), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - children: [ - Image.asset('assets/cheminova_logo.png'), - const Text('HELPING YOU GROW', - style: TextStyle( - color: Colors.black, - fontFamily: robotoFamily, - fontSize: 20, - fontWeight: FontWeight.w500)), - ], + backgroundColor: Colors.white, // Background color of the splash screen + body: Container( + width: MediaQuery.sizeOf(context).width, // Set width to full screen width + height: MediaQuery.sizeOf(context).height, // Set height to full screen height + padding: const EdgeInsets.only(top: 110, bottom: 50), // Padding for the content + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Space between children + children: [ + Column( + children: [ + Image.asset('assets/cheminova_logo.png'), // App logo + const Text( + 'HELPING YOU GROW', // Tagline text + style: TextStyle( + color: Colors.black, + fontFamily: robotoFamily, + fontSize: 20, + fontWeight: FontWeight.w500, ), - Image.asset('assets/plant.png'), - const Column( - children: [ - Text('Powered By', - style: TextStyle( - color: Colors.black, - fontSize: 12, - fontFamily: robotoFamily, - fontWeight: FontWeight.w500)), - Text('codeology.solutions', - style: TextStyle( - color: Colors.black, - fontFamily: robotoFamily, - fontSize: 14, - fontWeight: FontWeight.w700)), - ], + ), + ], + ), + Image.asset('assets/plant.png'), // Image displayed at the center + const Column( + children: [ + Text( + 'Powered By', // Label indicating the provider + style: TextStyle( + color: Colors.black, + fontSize: 12, + fontFamily: robotoFamily, + fontWeight: FontWeight.w500, ), - ]))); + ), + Text( + 'codeology.solutions', // Company name + style: TextStyle( + color: Colors.black, + fontFamily: robotoFamily, + fontSize: 14, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ], + ), + ), + ); } } diff --git a/lib/screens/summary_screen.dart b/lib/screens/summary_screen.dart index b1489b8..b3976a1 100644 --- a/lib/screens/summary_screen.dart +++ b/lib/screens/summary_screen.dart @@ -15,104 +15,108 @@ class SummaryScreen extends StatefulWidget { } class SummaryScreenState extends State { + // Controllers for managing text input fields final dateController = TextEditingController( - text: DateFormat('dd/MM/yyyy').format(DateTime.now())); + text: DateFormat('dd/MM/yyyy').format(DateTime.now())); // Default date set to today final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); // Default time set to now - final ProductController = TextEditingController(); - final liquidationController = TextEditingController(); - final dealercontroller = TextEditingController(); - final inventoryController = TextEditingController(); - final qualityController = TextEditingController(); - final salesController = TextEditingController(); - final skuController = TextEditingController(); + final ProductController = TextEditingController(); // Controller for product input + final liquidationController = TextEditingController(); // Controller for liquidation input + final dealercontroller = TextEditingController(); // Controller for dealer input + final inventoryController = TextEditingController(); // Controller for inventory input + final qualityController = TextEditingController(); // Controller for quality input + final salesController = TextEditingController(); // Controller for sales input + final skuController = TextEditingController(); // Controller for SKU input @override Widget build(BuildContext context) { return CommonBackground( child: Scaffold( - backgroundColor: Colors.transparent, + backgroundColor: Colors.transparent, // Transparent background appBar: CommonAppBar( actions: [ + // Back button in the app bar IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Navigate back to the previous screen }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), ), ], - title: const Text('Summary', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - backgroundColor: Colors.transparent, - elevation: 0, + title: const Text( + 'Summary', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek'), + ), + backgroundColor: Colors.transparent, // Transparent app bar background + elevation: 0, // No shadow ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Drawer for navigation body: Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), // Padding around the body content child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), // Enable bouncing scroll physics child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, // Center the content vertically + crossAxisAlignment: CrossAxisAlignment.center, // Center the content horizontally children: [ - const SizedBox(height: 16), + const SizedBox(height: 16), // Spacing Container( - padding: - const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), - // margin: const EdgeInsets.symmetric(horizontal: 30.0), + padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), + // Container styling decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), + border: Border.all(color: Colors.white), // White border + color: const Color(0xffB4D1E5).withOpacity(0.9), // Light blue background + borderRadius: BorderRadius.circular(26.0)), // Rounded corners child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align children to the start children: [ + // Input fields for summary data CommonTextFormField( title: 'Sales :', fillColor: Colors.white, - controller: salesController), + controller: salesController), // Sales input field const SizedBox(height: 15), CommonTextFormField( title: 'Inventory :', fillColor: Colors.white, - controller: inventoryController), + controller: inventoryController), // Inventory input field const SizedBox(height: 15), CommonTextFormField( title: 'Liquidation :', fillColor: Colors.white, - controller: liquidationController), + controller: liquidationController), // Liquidation input field const SizedBox(height: 15), CommonTextFormField( title: 'SKU :', fillColor: Colors.white, - controller: skuController), + controller: skuController), // SKU input field const SizedBox(height: 15), CommonTextFormField( title: 'Product :', fillColor: Colors.white, - controller: ProductController), + controller: ProductController), // Product input field const SizedBox(height: 15), Align( alignment: Alignment.center, child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'VIEW DATA', - backgroundColor: const Color(0xff004791), + borderRadius: 30, // Rounded corners for the button + width: double.infinity, // Full width button + height: kToolbarHeight - 10, // Button height + text: 'VIEW DATA', // Button text + backgroundColor: const Color(0xff004791), // Button background color onPressed: () { + // Navigate to DataSubmitSuccessFullScreen on button press Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen())); - - }) + }, ), - + ), ], ), ), diff --git a/lib/screens/update_inventory_screen.dart b/lib/screens/update_inventory_screen.dart index a56e741..b3e6d90 100644 --- a/lib/screens/update_inventory_screen.dart +++ b/lib/screens/update_inventory_screen.dart @@ -14,97 +14,111 @@ class UpdateInventoryScreen extends StatefulWidget { } class _UpdateInventoryScreenState extends State { - late PdRdProvider pdRdProvider; + late PdRdProvider pdRdProvider; // Provider for managing inventory data @override void initState() { super.initState(); - pdRdProvider = PdRdProvider(); + pdRdProvider = PdRdProvider(); // Initialize the provider } @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => pdRdProvider, - 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: const Text('Update Inventory Data', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - backgroundColor: Colors.transparent, - elevation: 0), - drawer: const CommonDrawer(), - body: Stack(children: [ - Consumer( - builder: (context, value, child) => Column(children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15.0, vertical: 25), - child: DropdownButtonFormField( - decoration: const InputDecoration( - fillColor: Colors.white, - filled: true, - border: OutlineInputBorder()), - value: value.selectedDistributorType, - items: [ - 'Principal Distributor', - 'Retail Distributor' - ].map((String type) { - return DropdownMenuItem( - value: type, child: Text(type)); - }).toList(), - hint: const Text('Select Distributor Type'), - onChanged: (val) => - value.updateDistributorType(val))), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15.0, vertical: 25), - child: DropdownButtonFormField( - decoration: const InputDecoration( - fillColor: Colors.white, - filled: true, - border: OutlineInputBorder()), - value: value.selectedPdRd, - items: value.pdRdList.map((e) { - return DropdownMenuItem( - value: e, child: Text(e.name ?? '')); - }).toList(), - onChanged: (val) => - value.updatePdRdValue(val), - isExpanded: true, - isDense: true, - iconSize: 24, - hint: - const Text('Select Distributor Name'))) - ])), - Consumer( - builder: (context, value, child) => value.isLoading - ? Container( - color: Colors.black12, - child: const Center( - child: CircularProgressIndicator())) - : const SizedBox()) - ])))); + create: (context) => pdRdProvider, // Provide the PdRdProvider to the widget tree + child: CommonBackground( + child: Scaffold( + backgroundColor: Colors.transparent, // Set the background to transparent + appBar: CommonAppBar( + actions: [ + // Back button in the app bar + IconButton( + onPressed: () => Navigator.pop(context), // Navigate back + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: const Text('Update Inventory Data', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), // Title of the app bar + backgroundColor: Colors.transparent, // Transparent app bar background + elevation: 0, // No shadow for the app bar + ), + drawer: const CommonDrawer(), // Drawer for navigation + body: Stack( + children: [ + Consumer( + builder: (context, value, child) => Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25), + child: DropdownButtonFormField( + decoration: const InputDecoration( + fillColor: Colors.white, + filled: true, + border: OutlineInputBorder()), + value: value.selectedDistributorType, // Currently selected distributor type + items: [ + 'Principal Distributor', + 'Retail Distributor' + ].map((String type) { + return DropdownMenuItem( + value: type, child: Text(type)); // Create dropdown items + }).toList(), + hint: const Text('Select Distributor Type'), // Hint text + onChanged: (val) => + value.updateDistributorType(val), // Update selected distributor type + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25), + child: DropdownButtonFormField( + decoration: const InputDecoration( + fillColor: Colors.white, + filled: true, + border: OutlineInputBorder()), + value: value.selectedPdRd, // Currently selected distributor + items: value.pdRdList.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name ?? '')); // Create dropdown items + }).toList(), + onChanged: (val) => + value.updatePdRdValue(val), // Update selected distributor value + isExpanded: true, + isDense: true, + iconSize: 24, + hint: const Text('Select Distributor Name'), // Hint text + ), + ) + ], + ), + ), + Consumer( + builder: (context, value, child) => value.isLoading + ? Container( + color: Colors.black12, + child: const Center( + child: CircularProgressIndicator())) // Show loading indicator + : const SizedBox(), + ) + ], + ), + ), + ), + ); } } +// Product model class to represent product details class Product { - final String name; - final String sku; - final bool isPurchased; - int? sale; - int? inventory; + final String name; // Product name + final String sku; // Stock keeping unit + final bool isPurchased; // Purchase status + int? sale; // Sales count + int? inventory; // Inventory count Product({ required this.name, @@ -115,8 +129,9 @@ class Product { }); } +// Widget to represent a block of product details class ProductBlock extends StatefulWidget { - final Product product; + final Product product; // Product data passed to the widget const ProductBlock({super.key, required this.product}); @@ -125,30 +140,30 @@ class ProductBlock extends StatefulWidget { } class _ProductBlockState extends State { - final saleController = TextEditingController(); - final inventoryController = TextEditingController(); - String? errorMessage; + final saleController = TextEditingController(); // Controller for sale input + final inventoryController = TextEditingController(); // Controller for inventory input + String? errorMessage; // Variable to hold error messages @override void initState() { super.initState(); + // Initialize controllers with product data saleController.text = widget.product.sale?.toString() ?? ''; inventoryController.text = widget.product.inventory?.toString() ?? ''; } void validateInput() { setState(() { - if (saleController.text.isNotEmpty && - inventoryController.text.isNotEmpty) { - int sale = int.parse(saleController.text); - int inventory = int.parse(inventoryController.text); + if (saleController.text.isNotEmpty && inventoryController.text.isNotEmpty) { + int sale = int.parse(saleController.text); // Parse sale input + int inventory = int.parse(inventoryController.text); // Parse inventory input if (inventory > sale) { - errorMessage = 'Inventory should be less than or equal to sales'; + errorMessage = 'Inventory should be less than or equal to sales'; // Error message } else { - errorMessage = null; + errorMessage = null; // Clear error message if valid } } else { - errorMessage = null; + errorMessage = null; // Clear error message if fields are empty } }); } @@ -156,38 +171,36 @@ class _ProductBlockState extends State { @override Widget build(BuildContext context) { return Card( - color: !widget.product.isPurchased ? Colors.white54 : Colors.white, + color: !widget.product.isPurchased ? Colors.white54 : Colors.white, // Change color based on purchase status margin: const EdgeInsets.all(8), child: Padding( padding: const EdgeInsets.all(16), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, // Align children to the start children: [ - Text('Product: ${widget.product.name}', - style: const TextStyle(fontSize: 16)), - Text('SKU: ${widget.product.sku}', - style: const TextStyle(fontSize: 15)), + Text('Product: ${widget.product.name}', style: const TextStyle(fontSize: 16)), // Display product name + Text('SKU: ${widget.product.sku}', style: const TextStyle(fontSize: 15)), // Display SKU const SizedBox(height: 8), TextField( - controller: saleController, - decoration: const InputDecoration(labelText: 'Sale'), - keyboardType: TextInputType.number, - enabled: widget.product.isPurchased, - onChanged: (_) => validateInput(), + controller: saleController, // Controller for sale input field + decoration: const InputDecoration(labelText: 'Sale'), // Label for sale input + keyboardType: TextInputType.number, // Numeric keyboard + enabled: widget.product.isPurchased, // Enable input based on purchase status + onChanged: (_) => validateInput(), // Validate input on change ), TextField( - controller: inventoryController, - decoration: const InputDecoration(labelText: 'Inventory'), - keyboardType: TextInputType.number, - enabled: widget.product.isPurchased, - onChanged: (_) => validateInput(), + controller: inventoryController, // Controller for inventory input field + decoration: const InputDecoration(labelText: 'Inventory'), // Label for inventory input + keyboardType: TextInputType.number, // Numeric keyboard + enabled: widget.product.isPurchased, // Enable input based on purchase status + onChanged: (_) => validateInput(), // Validate input on change ), - if (errorMessage != null) + if (errorMessage != null) // Display error message if exists Padding( padding: const EdgeInsets.only(top: 8.0), child: Text( errorMessage!, - style: const TextStyle(color: Colors.red), + style: const TextStyle(color: Colors.red), // Style for error message ), ), ], diff --git a/lib/screens/upload_documents_screen.dart b/lib/screens/upload_documents_screen.dart index 810f477..e8bcf63 100644 --- a/lib/screens/upload_documents_screen.dart +++ b/lib/screens/upload_documents_screen.dart @@ -17,35 +17,38 @@ class UploadDocumentScreen extends StatefulWidget { } class UploadDocumentScreenState extends State { + // Function to build the upload button and file view Widget _buildUploadButton(String title, File? file, bool isOptional) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Display title with optional label if applicable Text( title + (isOptional ? ' (optional)' : ''), style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - /// Create a view for the selected file + // Create a view for the selected file if (file != null) Container( height: 100, width: 100, decoration: BoxDecoration( - border: Border.all(color: Colors.grey), - borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey), // Border for the file container + borderRadius: BorderRadius.circular(8), // Rounded corners ), child: file.path.endsWith('.jpg') || file.path.endsWith('.png') - ? Image.file(file, fit: BoxFit.cover) + ? Image.file(file, fit: BoxFit.cover) // Display image if it's a valid format : Center( - child: Text(file.path.split('/').last, - style: const TextStyle( - fontSize: 12, fontWeight: FontWeight.bold), - textAlign: TextAlign.center))) + child: Text(file.path.split('/').last, // Show file name if not an image + style: const TextStyle( + fontSize: 12, fontWeight: FontWeight.bold), + textAlign: TextAlign.center))) else - const Text('No file selected', style: TextStyle(color: Colors.red)), + const Text('No file selected', style: TextStyle(color: Colors.red)), // Message when no file is selected + // Display uploaded file name if (file != null) Padding( padding: const EdgeInsets.only(top: 8.0), @@ -57,8 +60,10 @@ class UploadDocumentScreenState extends State { borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, + // Change button text based on file presence text: file == null ? 'Upload $title' : 'Change $title', backgroundColor: const Color(0xff004791), + // Trigger the image picker on button press onPressed: () => value.showPicker(context, title), ), ), @@ -73,7 +78,7 @@ class UploadDocumentScreenState extends State { builder: (context, value, child) => Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), // Enable bouncing scroll child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -81,26 +86,22 @@ class UploadDocumentScreenState extends State { const SizedBox(height: 16), Container( padding: - const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), + const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), decoration: BoxDecoration( border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), + color: const Color(0xffB4D1E5).withOpacity(0.9), // Background color for the upload section borderRadius: BorderRadius.circular(26.0)), child: Consumer( builder: (context, value, child) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Build upload buttons for each document type _buildUploadButton('PAN Card', value.panCard, false), - _buildUploadButton( - 'Aadhar Card', value.aadharCard, false), - _buildUploadButton( - 'GST Registration', value.gstRegistration, false), - _buildUploadButton( - 'Pesticide License', value.pesticideLicense, false), - _buildUploadButton( - 'Fertilizer License', value.fertilizerLicense, true), - _buildUploadButton('Selfie of Entrance Board', - value.selfieEntranceBoard, false), + _buildUploadButton('Aadhar Card', value.aadharCard, false), + _buildUploadButton('GST Registration', value.gstRegistration, false), + _buildUploadButton('Pesticide License', value.pesticideLicense, false), + _buildUploadButton('Fertilizer License', value.fertilizerLicense, true), + _buildUploadButton('Selfie of Entrance Board', value.selfieEntranceBoard, false), const SizedBox(height: 30), Align( alignment: Alignment.center, @@ -108,11 +109,11 @@ class UploadDocumentScreenState extends State { borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, - text: 'SUBMIT', + text: 'SUBMIT', // Submit button backgroundColor: const Color(0xff004791), onPressed: () { // Handle form submission - value.tabController.animateTo(2); + value.tabController.animateTo(2); // Navigate to the next tab }, ), ), diff --git a/lib/screens/verification_documents_screen.dart b/lib/screens/verification_documents_screen.dart index 436cacf..5edde39 100644 --- a/lib/screens/verification_documents_screen.dart +++ b/lib/screens/verification_documents_screen.dart @@ -12,90 +12,87 @@ class VerifyDocumentsScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - drawer: const CommonDrawer(), - body: CommonBackground( + drawer: const CommonDrawer(), // Drawer for navigation + body: CommonBackground( // Background widget for styling child: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), // Enable bouncing effect on scroll child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Consumer( + // Section for retailer details + Consumer( // Listen to changes in the KYC provider builder: (BuildContext context, CollectKycProvider value, - Widget? child) => + Widget? child) => _buildSection( - 'Retailer Details', - { - 'Trade Name': value.tradeNameController.text, - 'Name': value.nameController.text, - 'Address': value.addressController.text, - 'Town/City': value.city.text, - 'District': value.districtController.text, - 'State': value.state.text, - 'Pincode': value.pinCodeController.text, - 'Mobile Number': value.mobileNumberController.text, - 'Aadhar Number': value.aadharNumberController.text, - 'PAN Number': value.panNumberController.text, - 'GST Number': value.gstNumberController.text, - 'Mapped Principal Distributor': + 'Retailer Details', // Title for the section + { + 'Trade Name': value.tradeNameController.text, + 'Name': value.nameController.text, + 'Address': value.addressController.text, + 'Town/City': value.city.text, + 'District': value.districtController.text, + 'State': value.state.text, + 'Pincode': value.pinCodeController.text, + 'Mobile Number': value.mobileNumberController.text, + 'Aadhar Number': value.aadharNumberController.text, + 'PAN Number': value.panNumberController.text, + 'GST Number': value.gstNumberController.text, + 'Mapped Principal Distributor': value.selectedDistributor ?? '', - }, - onEdit: () { - value.tabController.animateTo(0); - // Handle edit for retailer details - debugPrint('Edit retailer details'); - }, - ), + }, + onEdit: () { + value.tabController.animateTo(0); // Navigate to the first tab for editing + debugPrint('Edit retailer details'); // Debug message for tracking + }, + ), ), const SizedBox(height: 20), - Consumer( + // Section for uploaded documents + Consumer( // Listen to changes in the KYC provider builder: (BuildContext context, CollectKycProvider value, - Widget? child) => + Widget? child) => _buildSection( - 'Uploaded Documents', - { - 'PAN Card': + 'Uploaded Documents', // Title for the section + { + 'PAN Card': value.panCard?.path.split('/').last ?? 'Not uploaded', - 'Aadhar Card': value.aadharCard?.path.split('/').last ?? - 'Not uploaded', - 'GST Registration': + 'Aadhar Card': value.aadharCard?.path.split('/').last ?? + 'Not uploaded', + 'GST Registration': value.gstRegistration?.path.split('/').last ?? 'Not uploaded', - 'Pesticide License': + 'Pesticide License': value.pesticideLicense?.path.split('/').last ?? 'Not uploaded', - 'Fertilizer License': + 'Fertilizer License': value.fertilizerLicense?.path.split('/').last ?? 'Not uploaded', - 'Selfie of Entrance Board': + 'Selfie of Entrance Board': value.selfieEntranceBoard?.path.split('/').last ?? 'Not uploaded', - }, - onEdit: () { - value.tabController.animateTo(1); - - // Handle edit for uploaded documents - debugPrint('Edit uploaded documents'); - }, - ), + }, + onEdit: () { + value.tabController.animateTo(1); // Navigate to the second tab for editing + debugPrint('Edit uploaded documents'); // Debug message for tracking + }, + ), ), const SizedBox(height: 30), + // Button to save and confirm the details Align( alignment: Alignment.center, - child: Consumer( - builder: (context, value, child) => CommonElevatedButton( + child: Consumer( // Listen to changes in the KYC provider + builder: (context, value, child) => CommonElevatedButton( borderRadius: 30, width: double.infinity, height: kToolbarHeight - 10, - text: ('SAVE AND CONFIRM'), + text: 'SAVE AND CONFIRM', // Button text backgroundColor: const Color(0xff004791), onPressed: () { - // Handle final submission - - value.validateFields(context); - - + // Handle final submission of KYC details + value.validateFields(context); // Validate fields before submission }, ), ), @@ -108,14 +105,14 @@ class VerifyDocumentsScreen extends StatelessWidget { ); } + // Method to build each section of the screen Widget _buildSection(String title, Map details, {required VoidCallback onEdit}) { return Container( padding: const EdgeInsets.all(20.0), - // margin: const EdgeInsets.symmetric(horizontal: 30.0), decoration: BoxDecoration( border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), + color: const Color(0xffB4D1E5).withOpacity(0.9), // Background color for sections borderRadius: BorderRadius.circular(24.0), ), child: Column( @@ -125,24 +122,25 @@ class VerifyDocumentsScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - title, + title, // Title of the section style: - const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ElevatedButton( - onPressed: onEdit, + onPressed: onEdit, // Trigger edit function style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xffFFFFFF), + backgroundColor: const Color(0xffFFFFFF), // White background for the edit button elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), - child: const Text('Edit'), + child: const Text('Edit'), // Edit button text ), ], ), const SizedBox(height: 10), + // Build detail rows from the provided details map ...details.entries .map((entry) => _buildDetailRow(entry.key, entry.value)), ], @@ -150,6 +148,7 @@ class VerifyDocumentsScreen extends StatelessWidget { ); } + // Method to build a single detail row Widget _buildDetailRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 5.0), @@ -159,16 +158,16 @@ class VerifyDocumentsScreen extends StatelessWidget { Expanded( flex: 2, child: Text( - label, + label, // Label for the detail style: const TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 3, child: Text( - value, + value, // Value for the detail style: TextStyle( - color: value == 'Not uploaded' ? Colors.red : Colors.black), + color: value == 'Not uploaded' ? Colors.red : Colors.black), // Change color if not uploaded )), ], ), diff --git a/lib/screens/verify_code_screen.dart b/lib/screens/verify_code_screen.dart index b942feb..29a3dd4 100644 --- a/lib/screens/verify_code_screen.dart +++ b/lib/screens/verify_code_screen.dart @@ -60,6 +60,7 @@ class _VerifyCodeScreenState extends State { ), ); + // Function to submit the entered OTP void _submitOtp() { if (_enteredOtp == "123456") { Navigator.push( @@ -123,6 +124,7 @@ class _VerifyCodeScreenState extends State { ), ), const Align( + // Message prompting the user to verify OTP alignment: Alignment.center, child: Text( 'OTP has sent to your registered \nmobile number, Please verify', @@ -135,6 +137,7 @@ class _VerifyCodeScreenState extends State { ), ), const SizedBox(height: 20), + // Input field for OTP Pinput( onTapOutside: (event) => FocusScope.of(context).unfocus(), length: 6, @@ -147,6 +150,7 @@ class _VerifyCodeScreenState extends State { onCompleted: (pin) => print(pin), ), const SizedBox(height: 20), + // Countdown timer for resending OTP Align( alignment: Alignment.center, child: Text( @@ -160,6 +164,7 @@ class _VerifyCodeScreenState extends State { ), ), const SizedBox(height: 40), + // Button to verify the entered OTP Align( alignment: Alignment.center, child: CommonElevatedButton( diff --git a/lib/screens/verify_phone_screen.dart b/lib/screens/verify_phone_screen.dart index cf958ff..abf8426 100644 --- a/lib/screens/verify_phone_screen.dart +++ b/lib/screens/verify_phone_screen.dart @@ -4,9 +4,9 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - import '../widgets/common_text_form_field.dart'; +// Main widget for the phone number verification screen class VerifyPhoneScreen extends StatefulWidget { const VerifyPhoneScreen({super.key}); @@ -17,16 +17,18 @@ class VerifyPhoneScreen extends StatefulWidget { class _VerifyPhoneScreenState extends State { @override Widget build(BuildContext context) { - + // Builds the phone verification screen return CommonBackground( isFullWidth: false, child: Scaffold( backgroundColor: Colors.transparent, appBar: AppBar( - leading: InkWell( - onTap: () => Navigator.pop(context), - child: Image.asset('assets/Back_attendance.png')), - backgroundColor: Colors.transparent), + leading: InkWell( + onTap: () => Navigator.pop(context), // Navigate back + child: Image.asset('assets/Back_attendance.png'), + ), + backgroundColor: Colors.transparent, + ), body: Center( child: SingleChildScrollView( child: Container( @@ -40,44 +42,47 @@ class _VerifyPhoneScreenState extends State { child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ + // Phone icon Align( alignment: Alignment.topLeft, child: Image.asset( 'assets/phone.png', - height: 50.0, // Adjust the height as needed - width: 50.0, // Adjust the width as needed + height: 50.0, + width: 50.0, ), ), const SizedBox(height: 20), - const Text('Verify Phone Number', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Anek', - )), - const SizedBox( - height: 10, + // Title for the screen + const Text( + 'Verify Phone Number', + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w500, + fontFamily: 'Anek', + ), ), - const Align( - alignment: Alignment.topLeft, - child: Text( - 'Please enter your phone number\nto receive one time password', - style: TextStyle( - fontSize: 14, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek', - )), + const SizedBox(height: 10), + // Instructional text + const Text( + 'Please enter your phone number\nto receive one time password', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek', + ), ), const SizedBox(height: 25), + // Input field for mobile number CommonTextFormField( - title: ' Enter Your Mobile Number', - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - keyboardType: TextInputType.number), + title: ' Enter Your Mobile Number', + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + keyboardType: TextInputType.number, + ), const SizedBox(height: 60), + // Button to submit phone number and request OTP Align( alignment: Alignment.center, child: CommonElevatedButton( @@ -87,13 +92,14 @@ class _VerifyPhoneScreenState extends State { height: kToolbarHeight - 10, text: 'GET OTP', onPressed: () { + // Navigate to OTP screen Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const VerifyCodeScreen())); - // Handle OTP submission here - print('OTP submitted'); + context, + MaterialPageRoute( + builder: (context) => const VerifyCodeScreen(), + ), + ); + print('OTP submitted'); // Debugging line }, ), ), diff --git a/lib/screens/verify_successfull_screen.dart b/lib/screens/verify_successfull_screen.dart index f05f5cc..4992b02 100644 --- a/lib/screens/verify_successfull_screen.dart +++ b/lib/screens/verify_successfull_screen.dart @@ -2,6 +2,7 @@ import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:flutter/material.dart'; +// Screen displayed after successful verification class VerifySuccessFullScreen extends StatefulWidget { const VerifySuccessFullScreen({super.key}); @@ -13,26 +14,39 @@ class _VerifySuccessFullScreenState extends State { @override void initState() { super.initState(); + // Navigate to the home screen after a delay of 2 seconds Future.delayed(const Duration(seconds: 2), () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage())); }); } + @override Widget build(BuildContext context) { - return Scaffold( - body: CommonBackground( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Verification successful', - style: TextStyle( - fontSize: 36, - color: Colors.white, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - const SizedBox(height: 20), // Add some space between the text and the image - Image.asset('assets/check_circle.png', )])))); + // Build the verification success screen + return Scaffold( + body: CommonBackground( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Display success message + const Text( + 'Verification successful', + style: TextStyle( + fontSize: 36, + color: Colors.white, + fontWeight: FontWeight.w400, + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 20), // Add space between the text and the image + // Display check circle image + Image.asset('assets/check_circle.png'), + ], + ), + ), + ), + ); } } diff --git a/lib/screens/view_pdf_screen.dart b/lib/screens/view_pdf_screen.dart index 008bac5..ee6cd8b 100644 --- a/lib/screens/view_pdf_screen.dart +++ b/lib/screens/view_pdf_screen.dart @@ -2,6 +2,7 @@ import 'package:cheminova/models/product_manual_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cached_pdfview/flutter_cached_pdfview.dart'; +// Screen to display a PDF document for a product manual class ViewPdfScreen extends StatefulWidget { final ProductManualModel productManualModel; @@ -14,17 +15,23 @@ class ViewPdfScreen extends StatefulWidget { class _ViewPdfScreenState extends State { @override Widget build(BuildContext context) { + // Build the PDF viewing screen return Scaffold( - body: SafeArea( - child: const PDF( - fitEachPage: true, - fitPolicy: FitPolicy.BOTH, - autoSpacing: false) - .cachedFromUrl( - widget.productManualModel.productManualDetail.url, - placeholder: (progress) => - Center(child: Text('$progress %')), - errorWidget: (error) => - Center(child: Text(error.toString()))))); + body: SafeArea( + // Use the PDF widget to display the PDF document + child: const PDF( + fitEachPage: true, // Fit the PDF content to each page + fitPolicy: FitPolicy.BOTH, // Adjust both width and height + autoSpacing: false, // Disable auto spacing + ).cachedFromUrl( + // Fetch and cache the PDF from the provided URL + widget.productManualModel.productManualDetail.url, + placeholder: (progress) => + Center(child: Text('$progress %')), // Display loading progress + errorWidget: (error) => + Center(child: Text(error.toString())), // Display error message + ), + ), + ); } } diff --git a/lib/screens/visit_rd_pd_screen.dart b/lib/screens/visit_rd_pd_screen.dart index 51a7ca6..ba0e169 100644 --- a/lib/screens/visit_rd_pd_screen.dart +++ b/lib/screens/visit_rd_pd_screen.dart @@ -9,10 +9,12 @@ import '../widgets/common_elevated_button.dart'; import '../widgets/common_text_form_field.dart'; import 'package:image_picker/image_picker.dart'; +// Screen for visiting dealers and submitting visit details class VisitDealersScreen extends StatefulWidget { - final String? tradeName; - final String? id; - final String? type; + final String? tradeName; // Name of the trade + final String? id; // ID of the visit + final String? type; // Type of the visit + const VisitDealersScreen({super.key, required this.tradeName, this.id, this.type}); @override @@ -20,37 +22,37 @@ class VisitDealersScreen extends StatefulWidget { } class VisitDealersScreenState extends State { - late VisitPdRdProvider _visitPdRdProvider; + late VisitPdRdProvider _visitPdRdProvider; // Provider for managing visit data final dateController = TextEditingController( - text: DateFormat('dd/MM/yyyy').format(DateTime.now())); + text: DateFormat('dd/MM/yyyy').format(DateTime.now())); // Controller for date input final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); // Controller for time input - final notesController = TextEditingController(); - final dealerController = TextEditingController(); - final meetingSummaryController = TextEditingController(); - final followUpActionsController = TextEditingController(); - final nextVisitDateController = TextEditingController(); - late TextEditingController retailerController = TextEditingController(); + final notesController = TextEditingController(); // Controller for notes + final dealerController = TextEditingController(); // Controller for dealer input + final meetingSummaryController = TextEditingController(); // Controller for meeting summary + final followUpActionsController = TextEditingController(); // Controller for follow-up actions + final nextVisitDateController = TextEditingController(); // Controller for next visit date + late TextEditingController retailerController = TextEditingController(); // Controller for retailer input - String selectedPurpose = 'Sales'; - List purposeOptions = ['Sales', 'Dues collection', 'Others']; + String selectedPurpose = 'Sales'; // Default selected purpose for the visit + List purposeOptions = ['Sales', 'Dues collection', 'Others']; // Options for visit purpose + // Function to pick an image from the camera or gallery Future _pickImage(ImageSource source) async { final ImagePicker picker = ImagePicker(); - final XFile? image = await picker.pickImage(source: source); + final XFile? image = await picker.pickImage(source: source); // Pick image from specified source if (image != null) { // Handle the picked image - // For example, you could update a state variable or send it to your provider - print('Image picked: ${image.path}'); - // You might want to update your UI to show the selected image + print('Image picked: ${image.path}'); // Log the picked image path setState(() { - notesController.text = image.path; // Just for demonstration + notesController.text = image.path; // Update notes with image path }); } } + // Function to show action sheet for selecting image source void _showImageSourceActionSheet() { showModalBottomSheet( context: context, @@ -59,19 +61,19 @@ class VisitDealersScreenState extends State { child: Wrap( children: [ ListTile( - leading: const Icon(Icons.camera_alt), - title: const Text('Take a photo'), + leading: const Icon(Icons.camera_alt), // Icon for camera + title: const Text('Take a photo'), // Option to take a photo onTap: () { - Navigator.pop(context); - _pickImage(ImageSource.camera); + Navigator.pop(context); // Close the bottom sheet + _pickImage(ImageSource.camera); // Pick image from camera }, ), ListTile( - leading: const Icon(Icons.photo_library), - title: const Text('Choose from gallery'), + leading: const Icon(Icons.photo_library), // Icon for gallery + title: const Text('Choose from gallery'), // Option to choose from gallery onTap: () { - Navigator.pop(context); - _pickImage(ImageSource.gallery); + Navigator.pop(context); // Close the bottom sheet + _pickImage(ImageSource.gallery); // Pick image from gallery }, ), ], @@ -81,43 +83,43 @@ class VisitDealersScreenState extends State { ); } + // Function to select the next visit date Future _selectNextVisitDate() async { final DateTime? picked = await showDatePicker( context: context, - initialDate: DateTime.now(), - firstDate: DateTime.now(), - lastDate: DateTime.now().add(const Duration(days: 365)), + initialDate: DateTime.now(), // Set initial date to today + firstDate: DateTime.now(), // Allow selecting today or later + lastDate: DateTime.now().add(const Duration(days: 365)), // Limit selection to one year from today ); if (picked != null) { setState(() { - nextVisitDateController.text = DateFormat('dd/MM/yyyy').format(picked); + nextVisitDateController.text = DateFormat('dd/MM/yyyy').format(picked); // Update controller with selected date }); } } @override void initState() { - _visitPdRdProvider = VisitPdRdProvider(); + _visitPdRdProvider = VisitPdRdProvider(); // Initialize provider super.initState(); } @override Widget build(BuildContext context) { - retailerController = TextEditingController(text: widget.tradeName); + retailerController = TextEditingController(text: widget.tradeName); // Initialize retailer controller with trade name return ChangeNotifierProvider( - create: (context) => _visitPdRdProvider, + create: (context) => _visitPdRdProvider, // Provide visit data to the widget tree child: CommonBackground( child: Stack( children: [ Scaffold( - backgroundColor: Colors.transparent, appBar: CommonAppBar( actions: [ IconButton( onPressed: () { - Navigator.pop(context); + Navigator.pop(context); // Back navigation }, icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20), @@ -131,7 +133,7 @@ class VisitDealersScreenState extends State { color: Colors.black, fontWeight: FontWeight.w400, fontFamily: 'Anek')), - Text(widget.tradeName??'', + Text(widget.tradeName ?? '', // Display trade name style: const TextStyle( fontSize: 20, color: Colors.black, @@ -142,7 +144,7 @@ class VisitDealersScreenState extends State { backgroundColor: Colors.transparent, elevation: 0, ), - drawer: const CommonDrawer(), + drawer: const CommonDrawer(), // Drawer for navigation body: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Column( @@ -165,19 +167,19 @@ class VisitDealersScreenState extends State { readOnly: true, title: 'Select Retailer', fillColor: Colors.white, - controller: retailerController), + controller: retailerController), // Retailer selection field const SizedBox(height: 15), CommonTextFormField( title: 'Visit date', readOnly: true, fillColor: Colors.white, - controller: dateController), + controller: dateController), // Date field const SizedBox(height: 15), CommonTextFormField( title: 'Time', readOnly: true, fillColor: Colors.white, - controller: timeController), + controller: timeController), // Time field const SizedBox(height: 15), DropdownButtonFormField( decoration: const InputDecoration( @@ -185,7 +187,7 @@ class VisitDealersScreenState extends State { fillColor: Colors.white, filled: true, ), - value: selectedPurpose, + value: selectedPurpose, // Current selected purpose items: purposeOptions.map((String value) { return DropdownMenuItem( value: value, @@ -194,7 +196,7 @@ class VisitDealersScreenState extends State { }).toList(), onChanged: (String? newValue) { setState(() { - selectedPurpose = newValue!; + selectedPurpose = newValue!; // Update selected purpose }); }, ), @@ -203,22 +205,22 @@ class VisitDealersScreenState extends State { title: 'Meeting summary:', fillColor: Colors.white, maxLines: 3, - controller: meetingSummaryController), + controller: meetingSummaryController), // Summary of the meeting const SizedBox(height: 15), CommonTextFormField( title: 'Follow-up Actions:', fillColor: Colors.white, maxLines: 3, - controller: followUpActionsController), + controller: followUpActionsController), // Actions to follow up const SizedBox(height: 15), GestureDetector( - onTap: _selectNextVisitDate, + onTap: _selectNextVisitDate, // Handle date selection child: AbsorbPointer( child: CommonTextFormField( title: 'Next visit date:', readOnly: true, fillColor: Colors.white, - controller: nextVisitDateController, + controller: nextVisitDateController, // Next visit date field ), ), ), @@ -229,25 +231,29 @@ class VisitDealersScreenState extends State { child: CommonTextFormField( title: 'Attach Documents/Photos', fillColor: Colors.white, - controller: notesController), + controller: notesController), // Notes for documents/photos ), IconButton( icon: const Icon(Icons.add_a_photo), - onPressed: _showImageSourceActionSheet, + onPressed: _showImageSourceActionSheet, // Open image source selection ), ], ), const SizedBox(height: 15), - Consumer(builder: (context, value, child) => Align( - alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SUBMIT', - backgroundColor: const Color(0xff004791), - onPressed: () { - value.submitVisitPdRd(widget.id ?? '', type: widget.type, + Consumer( + builder: (context, value, child) => Align( + alignment: Alignment.center, + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SUBMIT', // Submit button + backgroundColor: const Color(0xff004791), + onPressed: () { + // Submit visit details to provider + value.submitVisitPdRd( + widget.id ?? '', + type: widget.type, retailerName: retailerController.text, visitDate: dateController.text, visitTime: timeController.text, @@ -255,11 +261,11 @@ class VisitDealersScreenState extends State { selectedPurpose: selectedPurpose, followUpActions: followUpActionsController.text, nextVisitDate: nextVisitDateController.text, - ); - }, + ); + }, + ), ), ), - ), ], ), ), @@ -267,20 +273,23 @@ class VisitDealersScreenState extends State { ), ), ), - Consumer(builder: (context, value, child) { - if (value.isLoading) { - return Container( - color: Colors.black.withOpacity(0.5), - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } - return const SizedBox.shrink(); - }), + // Loading overlay + Consumer( + builder: (context, value, child) { + if (value.isLoading) { + return Container( + color: Colors.black.withOpacity(0.5), + child: const Center( + child: CircularProgressIndicator(), // Loading indicator + ), + ); + } + return const SizedBox.shrink(); // Return empty widget when not loading + }, + ), ], ), ), ); } -} \ No newline at end of file +} diff --git a/lib/services/api_client.dart b/lib/services/api_client.dart index d53034d..e068201 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -4,46 +4,57 @@ import 'package:cheminova/services/api_urls.dart'; import 'package:flutter/material.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +// ApiClient class for making HTTP requests using Dio class ApiClient { - final Dio _dio; - final SecureStorageService _storageService = SecureStorageService(); + final Dio _dio; // Instance of Dio for making HTTP requests + final SecureStorageService _storageService = SecureStorageService(); // Service for secure storage of tokens + // Constructor for ApiClient ApiClient({String? baseUrl}) : _dio = Dio(BaseOptions( - baseUrl: baseUrl ?? ApiUrls.baseUrl, - connectTimeout: const Duration(seconds: 120), - receiveTimeout: const Duration(seconds: 120))) { + baseUrl: baseUrl ?? ApiUrls.baseUrl, // Set base URL for API + connectTimeout: const Duration(seconds: 120), // Timeout for connection + receiveTimeout: const Duration(seconds: 120))) { // Timeout for receiving data + + // Adding interceptors for logging requests and responses _dio.interceptors - .add(LogInterceptor(responseBody: true, requestBody: true)); - _dio.interceptors.add(PrettyDioLogger()); + .add(LogInterceptor(responseBody: true, requestBody: true)); // Log requests and responses + _dio.interceptors.add(PrettyDioLogger()); // Pretty logging for requests and responses + + // Adding an interceptor for request handling _dio.interceptors .add(InterceptorsWrapper(onRequest: (options, handler) async { + // Retrieve access token from secure storage String? token = await _storageService.read(key: 'access_token'); if (token != null) { - debugPrint('Token start ------------> $token <------------ Token end'); - options.headers['Authorization'] = 'Bearer $token'; + debugPrint('Token start ------------> $token <------------ Token end'); // Debug log for token + options.headers['Authorization'] = 'Bearer $token'; // Add token to request headers } - return handler.next(options); + return handler.next(options); // Proceed to the next interceptor or request }, onResponse: (response, handler) { - return handler.next(response); + return handler.next(response); // Proceed to the next interceptor or response }, onError: (DioException e, handler) { - return handler.next(e); + return handler.next(e); // Handle errors and proceed })); } + // Method for making GET requests Future get(String path, {Map? queryParameters}) { - return _dio.get(path, queryParameters: queryParameters); + return _dio.get(path, queryParameters: queryParameters); // Send GET request } + // Method for making POST requests Future post(String path, {dynamic data}) { - return _dio.post(path, data: data); + return _dio.post(path, data: data); // Send POST request } + // Method for making PUT requests Future put(String path, {Map? data}) { - return _dio.put(path, data: data); + return _dio.put(path, data: data); // Send PUT request } + // Method for making DELETE requests Future delete(String path, {Map? data}) { - return _dio.delete(path, data: data); + return _dio.delete(path, data: data); // Send DELETE request } }