Submit VisitUrl Added

This commit is contained in:
Vaibhav 2024-09-29 22:09:36 +05:30
parent d077d06c07
commit 86a9b751ba
40 changed files with 1778 additions and 1451 deletions

View File

@ -1,5 +1,4 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cheminova/constants/constant.dart'; import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/models/SalesTaskResponse.dart'; import 'package:cheminova/models/SalesTaskResponse.dart';
import 'package:cheminova/screens/data_submit_successfull.dart'; import 'package:cheminova/screens/data_submit_successfull.dart';

View File

@ -22,6 +22,7 @@ class CollectKycProvider extends ChangeNotifier {
late TabController tabController; late TabController tabController;
final _apiClient = ApiClient(); final _apiClient = ApiClient();
// Text controllers for form fields
final selectTaskController= TextEditingController(); final selectTaskController= TextEditingController();
final tradeNameController = TextEditingController(); final tradeNameController = TextEditingController();
final nameController = TextEditingController(); final nameController = TextEditingController();
@ -38,6 +39,7 @@ class CollectKycProvider extends ChangeNotifier {
String? selectedDistributor; String? selectedDistributor;
final retailerDetailsFormKey = GlobalKey<FormState>(); final retailerDetailsFormKey = GlobalKey<FormState>();
// Files to hold picked images for various documents
File? panCard; File? panCard;
File? aadharCard; File? aadharCard;
@ -47,6 +49,7 @@ class CollectKycProvider extends ChangeNotifier {
File? selfieEntranceBoard; File? selfieEntranceBoard;
final ImagePicker _picker = ImagePicker(); final ImagePicker _picker = ImagePicker();
// Method to pick an image from the gallery or camera
Future<void> pickImage(ImageSource source, String documentType) async { Future<void> pickImage(ImageSource source, String documentType) async {
final pickedFile = await _picker.pickImage(source: source); final pickedFile = await _picker.pickImage(source: source);
@ -75,6 +78,7 @@ class CollectKycProvider extends ChangeNotifier {
} }
notifyListeners(); notifyListeners();
} }
// Method to show image picker options (gallery/camera) in a modal
void showPicker(BuildContext context, String documentType) { void showPicker(BuildContext context, String documentType) {
showModalBottomSheet( showModalBottomSheet(
@ -145,6 +149,7 @@ class CollectKycProvider extends ChangeNotifier {
setLoading(false); setLoading(false);
} }
} }
// Method to validate form fields before submission
void validateFields(BuildContext context) { void validateFields(BuildContext context) {
if (tradeNameController.text.trim().isEmpty) { if (tradeNameController.text.trim().isEmpty) {
@ -225,6 +230,7 @@ class CollectKycProvider extends ChangeNotifier {
submitCollectKycForm(context); submitCollectKycForm(context);
} }
} }
// Method to submit the KYC form to the API
Future<void> submitCollectKycForm(BuildContext context) async { Future<void> submitCollectKycForm(BuildContext context) async {
final dio = Dio(); final dio = Dio();
@ -232,6 +238,8 @@ class CollectKycProvider extends ChangeNotifier {
final headers = {'Authorization': 'Bearer $token'}; final headers = {'Authorization': 'Bearer $token'};
// Construct the FormData // Construct the FormData
// Create multipart form data
final data = FormData.fromMap({ final data = FormData.fromMap({
'name': nameController.text.trim(), 'name': nameController.text.trim(),
'trade_name': tradeNameController.text.trim(), 'trade_name': tradeNameController.text.trim(),

View File

@ -6,60 +6,78 @@ import 'package:cheminova/services/api_urls.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// Provider class to manage the Principal Distributor (PD) and Retailer Distributor (RD) data
class PdRdProvider extends ChangeNotifier { 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<GetPdRdResponse> _pdRdList = []; List<GetPdRdResponse> _pdRdList = []; // List to store fetched PD/RD data
List<GetPdRdResponse> get pdRdList => _pdRdList; List<GetPdRdResponse> 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) { void setLoading(bool loading) {
_isLoading = 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<void> getPdRd() async { Future<void> getPdRd() async {
setLoading(true); setLoading(true); // Show loading indicator while data is being fetched
try { try {
// Make GET request to fetch PD/RD data, replacing spaces in the selected distributor type
Response response = await _apiClient.get( Response response = await _apiClient.get(
ApiUrls.getPdRdUrl + selectedDistributorType!.replaceAll(' ', '')); ApiUrls.getPdRdUrl + selectedDistributorType!.replaceAll(' ', ''));
setLoading(false);
setLoading(false); // Hide loading indicator after response
if (response.statusCode == 200) { if (response.statusCode == 200) {
// If response is successful, parse the data into a list of GetPdRdResponse objects
_pdRdList = (response.data as List) _pdRdList = (response.data as List)
.map((json) => GetPdRdResponse.fromJson(json)) .map((json) => GetPdRdResponse.fromJson(json))
.toList(); .toList();
notifyListeners(); notifyListeners(); // Notify listeners to refresh the UI with new data
} else { } else {
// Log an error message if the response is not successful
debugPrint("Failed to load data: ${response.statusCode}"); debugPrint("Failed to load data: ${response.statusCode}");
} }
} catch (e) { } catch (e) {
// Log any exceptions that occur during the API call
debugPrint("Error occurred: $e"); debugPrint("Error occurred: $e");
} finally { } finally {
// Always hide the loading indicator once the operation completes
setLoading(false); setLoading(false);
} }
} }
// Method to update the selected distributor type and fetch corresponding PD/RD data
void updateDistributorType(String? val) { void updateDistributorType(String? val) {
selectedDistributorType = val; selectedDistributorType = val; // Update selected distributor type
selectedPdRd = null; selectedPdRd = null; // Reset the selected PD/RD
notifyListeners(); notifyListeners(); // Notify listeners to update the UI
getPdRd(); 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) { updatePdRdValue(GetPdRdResponse? val) {
selectedPdRd = val; selectedPdRd = val; // Update selected PD/RD
notifyListeners(); notifyListeners(); // Notify listeners to update the UI
// Delay navigation slightly to ensure smooth transition
Future.delayed(const Duration(milliseconds: 500), () { Future.delayed(const Duration(milliseconds: 500), () {
if (selectedPdRd != null && selectedDistributorType != null) { if (selectedPdRd != null && selectedDistributorType != null) {
// Navigate to the AddProductsScreen, passing selected distributor type and PD/RD details
Navigator.push( Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
MaterialPageRoute( MaterialPageRoute(

View File

@ -7,23 +7,25 @@ import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../models/products_response.dart'; import '../models/products_response.dart';
// Provider class responsible for handling product data and API requests
class ProductProvider extends ChangeNotifier { class ProductProvider extends ChangeNotifier {
final _apiClient = ApiClient(); final _apiClient = ApiClient(); // API client for making HTTP requests
ProductResponse? productResponse; ProductResponse? productResponse; // Response object for product list
List<ProductModel> productList = []; List<ProductModel> productList = []; // List of products
List<ProductModel> searchList = []; List<ProductModel> 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<ProductModel> selectedProducts = []; // List of selected products
List<ProductModel> selectedProducts = [];
// Set loading state and notify listeners
void setLoading(bool loading) { void setLoading(bool loading) {
_isLoading = 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) { void filterProducts(String query) {
searchList = productList.where((product) { searchList = productList.where((product) {
final productNameLower = product.productName.toLowerCase(); final productNameLower = product.productName.toLowerCase();
@ -32,27 +34,29 @@ class ProductProvider extends ChangeNotifier {
return productNameLower.contains(searchLower) || return productNameLower.contains(searchLower) ||
productSkuLower.contains(searchLower); productSkuLower.contains(searchLower);
}).toList(); }).toList();
notifyListeners(); notifyListeners(); // Notify listeners to update UI with search results
} }
// Fetch the list of products from the API
Future<void> getProducts() async { Future<void> getProducts() async {
Response response = await _apiClient.get(ApiUrls.getProducts); Response response = await _apiClient.get(ApiUrls.getProducts);
debugPrint('Response: $response'); debugPrint('Response: $response');
setLoading(false); setLoading(false);
if (response.statusCode == 200) { if (response.statusCode == 200) {
productResponse = ProductResponse.fromJson(response.data); productResponse = ProductResponse.fromJson(response.data);
productList = productResponse!.products! productList = productResponse!.products!
.map((product) => .map((product) => ProductModel(sku: product.sKU!, productName: product.name!))
ProductModel(sku: product.sKU!, productName: product.name!)) .toList();
.toList(); notifyListeners(); // Notify listeners to update the UI with the product list
notifyListeners(); }
}
} }
// Submit selected products to the API
Future<void> submitProducts( Future<void> submitProducts(
{required String distributorType, required String pdRdId, String? inventoryId}) async { {required String distributorType, required String pdRdId, String? inventoryId}) async {
setLoading(true); setLoading(true);
try { try {
// Send POST request to submit products
Response response = await _apiClient.post(ApiUrls.submitProductUrl, Response response = await _apiClient.post(ApiUrls.submitProductUrl,
data: json.encode({ data: json.encode({
"addedFor": distributorType.replaceAll(' ', ''), "addedFor": distributorType.replaceAll(' ', ''),
@ -60,23 +64,26 @@ class ProductProvider extends ChangeNotifier {
"products": selectedProducts.map((e) => e.toJson()).toList() "products": selectedProducts.map((e) => e.toJson()).toList()
})); }));
setLoading(false); setLoading(false);
// Handle successful response
if (response.statusCode == 201) { if (response.statusCode == 201) {
ScaffoldMessenger.of( ScaffoldMessenger.of(
navigatorKey.currentContext!, navigatorKey.currentContext!,
).showSnackBar( ).showSnackBar(
SnackBar(content: Text(response.data['message'])), 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'); debugPrint('Task Updated');
if (value.statusCode == 200) { if (value.statusCode == 200) {
resetProducts(); resetProducts(); // Reset selected products and product list
Navigator.push( Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const DataSubmitSuccessFullScreen())); builder: (context) => const DataSubmitSuccessFullScreen()));
} } else {
else{
ScaffoldMessenger.of( ScaffoldMessenger.of(
navigatorKey.currentContext!, navigatorKey.currentContext!,
).showSnackBar( ).showSnackBar(
@ -84,8 +91,7 @@ class ProductProvider extends ChangeNotifier {
); );
} }
}); });
} } else {
else{
resetProducts(); resetProducts();
Navigator.push( Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
@ -95,30 +101,34 @@ class ProductProvider extends ChangeNotifier {
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false);
debugPrint("Error: $e"); debugPrint("Error: $e"); // Handle any errors during the request
} }
} }
// Reset selected products and product list
void resetProducts() { void resetProducts() {
selectedProducts.clear(); selectedProducts.clear();
productList.clear(); productList.clear();
productResponse = null; productResponse = null;
notifyListeners(); notifyListeners(); // Notify listeners to update the UI
} }
} }
// Model class for Product
class ProductModel { class ProductModel {
String sku; String sku; // SKU of the product
String productName; String productName; // Name of the product
int? sale; int? sale; // Sale quantity (optional)
int? inventory; int? inventory; // Inventory quantity (optional)
// Constructor for ProductModel
ProductModel( ProductModel(
{required this.sku, {required this.sku,
required this.productName, required this.productName,
this.sale, this.sale,
this.inventory}); this.inventory});
// Factory method to create a ProductModel from JSON
factory ProductModel.fromJson(Map<String, dynamic> json) { factory ProductModel.fromJson(Map<String, dynamic> json) {
return ProductModel( return ProductModel(
sku: json['SKU'], sku: json['SKU'],
@ -127,8 +137,9 @@ class ProductModel {
inventory: json['Inventory']); inventory: json['Inventory']);
} }
get comments => null; get comments => null; // Placeholder for any future comments functionality
// Convert ProductModel to JSON format for API requests
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'SKU': sku, 'SKU': sku,

View File

@ -5,35 +5,44 @@ import 'package:flutter/material.dart';
import '../services/api_client.dart'; import '../services/api_client.dart';
import '../services/api_urls.dart'; import '../services/api_urls.dart';
// Provider class responsible for managing rejected applications data and API calls
class RejectedProvider extends ChangeNotifier { class RejectedProvider extends ChangeNotifier {
// Constructor to automatically fetch rejected applications when the provider is created
RejectedProvider() { RejectedProvider() {
getRejectedApplication(); getRejectedApplication(); // Fetch rejected applications
} }
final _apiClient = ApiClient();
RejectedApplicationResponse? rejectedApplicationResponse;
List<RejectedApplicationResponse> rejectedApplicationList = [];
bool _isLoading = false; final _apiClient = ApiClient(); // API client instance for making HTTP requests
bool get isLoading => _isLoading; RejectedApplicationResponse? rejectedApplicationResponse; // Response object for rejected applications
List<RejectedApplicationResponse> 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) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
notifyListeners(); notifyListeners(); // Notify listeners that the loading state has changed
} }
// Fetch rejected applications from the API
Future<void> getRejectedApplication() async { Future<void> getRejectedApplication() async {
setLoading(true); setLoading(true); // Set loading state to true while fetching data
try { try {
// Send GET request to the API to fetch rejected applications
Response response = await _apiClient.get(ApiUrls.rejectedApplication); 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) { if (response.statusCode == 200) {
// Map each item in the response data to a RejectedApplicationResponse object
rejectedApplicationList = (response.data as List) rejectedApplicationList = (response.data as List)
.map((e) => RejectedApplicationResponse.fromJson(e)) .map((e) => RejectedApplicationResponse.fromJson(e))
.toList(); .toList();
notifyListeners(); notifyListeners(); // Notify listeners to update the UI with the new data
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false); // In case of error, set loading state to false
} }
} }
} }

View File

@ -6,40 +6,50 @@ import 'package:flutter/material.dart';
import '../services/api_client.dart'; import '../services/api_client.dart';
import '../services/api_urls.dart'; import '../services/api_urls.dart';
// Provider class responsible for managing tasks and fetching task data from the API
class SelectTaskProvider extends ChangeNotifier { class SelectTaskProvider extends ChangeNotifier {
// Constructor that automatically fetches tasks when the provider is initialized
SelectTaskProvider() { SelectTaskProvider() {
getTask(); getTask(); // Fetch tasks upon provider creation
} }
final _apiClient = ApiClient(); final _apiClient = ApiClient(); // API client instance for handling HTTP requests
List<Tasks> tasksList = []; List<Tasks> 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) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
notifyListeners(); notifyListeners(); // Notifies listeners that the loading state has changed
} }
// Function to fetch tasks from the API
Future<void> getTask() async { Future<void> getTask() async {
setLoading(true); setLoading(true); // Set loading to true when data fetching starts
try { try {
// Send GET request to the API to retrieve tasks
Response response = await _apiClient.get(ApiUrls.kycSelectTaskUrl); 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) { if (response.statusCode == 200) {
// Parse the response data into a SelectTaskKycResponse object
final data = SelectTaskKycResponse.fromJson(response.data); final data = SelectTaskKycResponse.fromJson(response.data);
// Filter tasks to include only those that are not completed
tasksList = data!.tasks! tasksList = data!.tasks!
.where( .where((element) => element.taskStatus!.toLowerCase() != "completed")
(element) => element.taskStatus!.toLowerCase() != "completed")
.toList(); .toList();
notifyListeners(); notifyListeners(); // Notify listeners to update the UI with the new tasks
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false); // Set loading to false if an error occurs
if (kDebugMode) { if (kDebugMode) {
print("Error occurred while fetching notifications: $e"); print("Error occurred while fetching tasks: $e"); // Log the error in debug mode
} }
} }
} }

View File

@ -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_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
@ -8,17 +9,20 @@ import 'package:provider/provider.dart';
import '../provider/products_provider.dart'; import '../provider/products_provider.dart';
// Stateful widget for the Add Products screen.
class AddProductsScreen extends StatefulWidget { class AddProductsScreen extends StatefulWidget {
final String distributorType; final String distributorType; // Type of distributor (e.g., Retailer, Wholesaler)
final String tradeName; final String tradeName; // Trade name of the distributor
final String pdRdId; final String pdRdId; // Principal distributor/retailer ID
final String? inventoryId; final String? inventoryId; // Optional inventory ID
// Constructor for AddProductsScreen, with required parameters.
const AddProductsScreen({ const AddProductsScreen({
super.key, super.key,
required this.distributorType, required this.distributorType,
required this.tradeName, required this.tradeName,
required this.pdRdId, this.inventoryId required this.pdRdId,
this.inventoryId,
}); });
@override @override
@ -26,11 +30,15 @@ class AddProductsScreen extends StatefulWidget {
} }
class _AddProductsScreenState extends State<AddProductsScreen> { class _AddProductsScreenState extends State<AddProductsScreen> {
// Controller for the search input field.
final searchController = TextEditingController(); final searchController = TextEditingController();
// ProductProvider for managing product-related logic.
late ProductProvider productProvider; late ProductProvider productProvider;
@override @override
void initState() { void initState() {
// Initialize the productProvider and fetch the products after the widget is built.
productProvider = Provider.of<ProductProvider>(context, listen: false); productProvider = Provider.of<ProductProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
productProvider.getProducts(); productProvider.getProducts();
@ -40,6 +48,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
@override @override
void dispose() { void dispose() {
// Reset the products list when the screen is disposed.
if (context.mounted) { if (context.mounted) {
productProvider.resetProducts(); productProvider.resetProducts();
} }
@ -48,6 +57,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Build the main UI for the screen.
return CommonBackground( return CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -73,6 +83,8 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
elevation: 0 elevation: 0
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
// BottomNavigationBar which holds the "Add Products" and "Submit" button.
bottomNavigationBar: Consumer<ProductProvider>( bottomNavigationBar: Consumer<ProductProvider>(
builder: (context, value, child) => Column( builder: (context, value, child) => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -86,6 +98,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Floating Action Button to add products
FloatingActionButton.extended( FloatingActionButton.extended(
onPressed: () { onPressed: () {
showModalBottomSheet( showModalBottomSheet(
@ -95,11 +108,13 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
), ),
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
// Display list of products with a search bar in the modal sheet.
return Consumer<ProductProvider>( return Consumer<ProductProvider>(
builder: (context, value, child) => StatefulBuilder( builder: (context, value, child) => StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
return Column( return Column(
children: [ children: [
// Search bar to filter products
Padding( Padding(
padding: const EdgeInsets.all(18.0), padding: const EdgeInsets.all(18.0),
child: TextField( child: TextField(
@ -115,18 +130,21 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
} }
) )
), ),
// List of products (filtered or all)
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: searchController.text.isEmpty itemCount: searchController.text.isEmpty
? value.productList.length ? value.productList.length
: value.searchList.length, : value.searchList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
// Check if the product is already selected.
bool isAlreadySelected = value.selectedProducts.any( bool isAlreadySelected = value.selectedProducts.any(
(selectedProduct) => selectedProduct.sku == value.productList[index].sku (selectedProduct) => selectedProduct.sku == value.productList[index].sku
); );
final data = searchController.text.isEmpty final data = searchController.text.isEmpty
? value.productList[index] ? value.productList[index]
: value.searchList[index]; : value.searchList[index];
// Product tile with name, SKU, and selection functionality.
return Card( return Card(
child: ListTile( child: ListTile(
title: Text( title: Text(
@ -160,6 +178,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
icon: const Icon(Icons.add, color: Colors.black), icon: const Icon(Icons.add, color: Colors.black),
label: const Text('Add Products', style: TextStyle(color: Colors.black)) label: const Text('Add Products', style: TextStyle(color: Colors.black))
), ),
// Display Submit button if products are selected.
if (value.selectedProducts.isNotEmpty) ...[ if (value.selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0), const SizedBox(height: 16.0),
Consumer<ProductProvider>( Consumer<ProductProvider>(
@ -169,6 +188,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'SUBMIT', text: 'SUBMIT',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
// Submit selected products, but first validate the data.
onPressed: () { onPressed: () {
if (value.selectedProducts.isNotEmpty && if (value.selectedProducts.isNotEmpty &&
value.selectedProducts.every((product) => value.selectedProducts.every((product) =>
@ -181,6 +201,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
pdRdId: widget.pdRdId, inventoryId: widget.inventoryId pdRdId: widget.pdRdId, inventoryId: widget.inventoryId
); );
} else { } else {
// Show error message if data is incomplete.
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Please fill out all product details, including sale and inventory.') content: Text('Please fill out all product details, including sale and inventory.')
@ -198,20 +219,24 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
], ],
), ),
), ),
// Main body of the screen that shows the selected products and the loader.
body: Consumer<ProductProvider>( body: Consumer<ProductProvider>(
builder: (context, value, child) { builder: (context, value, child) {
return Stack( return Stack(
children: [ children: [
Column( Column(
children: [ children: [
// Display the selected products in a list.
if (value.selectedProducts.isNotEmpty) if (value.selectedProducts.isNotEmpty)
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: value.selectedProducts.length, itemCount: value.selectedProducts.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
// Display individual product blocks.
return ProductBlock( return ProductBlock(
onUpdate: (updatedProduct) { onUpdate: (updatedProduct) {
debugPrint('selected product le ${value.selectedProducts.length}'); debugPrint('selected product length: ${value.selectedProducts.length}');
setState(() { setState(() {
value.selectedProducts[index] = updatedProduct; value.selectedProducts[index] = updatedProduct;
}); });
@ -228,6 +253,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
) )
] ]
), ),
// Show loader when products are being fetched.
(value.isLoading) (value.isLoading)
? Container( ? Container(
color: Colors.black12, color: Colors.black12,
@ -243,10 +269,11 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
} }
} }
// Widget to represent individual product blocks in the selected products list.
class ProductBlock extends StatefulWidget { class ProductBlock extends StatefulWidget {
final ProductModel product; final ProductModel product; // The product model to be displayed.
final ValueChanged<ProductModel> onUpdate; final ValueChanged<ProductModel> onUpdate; // Callback when product data is updated.
final VoidCallback onRemove; final VoidCallback onRemove; // Callback to remove the product.
const ProductBlock({ const ProductBlock({
super.key, super.key,
@ -260,17 +287,20 @@ class ProductBlock extends StatefulWidget {
} }
class _ProductBlockState extends State<ProductBlock> { class _ProductBlockState extends State<ProductBlock> {
// Controllers for sale and inventory text fields.
final saleController = TextEditingController(); final saleController = TextEditingController();
final inventoryController = TextEditingController(); final inventoryController = TextEditingController();
String? errorMessage; String? errorMessage; // Error message for input validation.
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Initialize controllers with existing product data.
saleController.text = (widget.product.sale ?? '').toString(); saleController.text = (widget.product.sale ?? '').toString();
inventoryController.text = (widget.product.inventory ?? '').toString(); inventoryController.text = (widget.product.inventory ?? '').toString();
} }
// Validate the inputs for sale and inventory fields.
void validateInput() { void validateInput() {
setState(() { setState(() {
String? saleError; String? saleError;
@ -289,6 +319,7 @@ class _ProductBlockState extends State<ProductBlock> {
int sale = int.parse(saleController.text); int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text); int inventory = int.parse(inventoryController.text);
// Update the product with validated data.
widget.onUpdate(ProductModel( widget.onUpdate(ProductModel(
sku: widget.product.sku, sku: widget.product.sku,
productName: widget.product.productName, productName: widget.product.productName,
@ -321,6 +352,7 @@ class _ProductBlockState extends State<ProductBlock> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Text field for sale input.
TextField( TextField(
controller: saleController, controller: saleController,
onTapOutside: (event) => FocusScope.of(context).unfocus(), onTapOutside: (event) => FocusScope.of(context).unfocus(),
@ -335,6 +367,7 @@ class _ProductBlockState extends State<ProductBlock> {
enabled: true, enabled: true,
onChanged: (_) => validateInput() onChanged: (_) => validateInput()
), ),
// Text field for inventory input.
TextField( TextField(
controller: inventoryController, controller: inventoryController,
onTapOutside: (event) => FocusScope.of(context).unfocus(), onTapOutside: (event) => FocusScope.of(context).unfocus(),
@ -354,6 +387,7 @@ class _ProductBlockState extends State<ProductBlock> {
] ]
), ),
), ),
// Remove button for the product block.
Positioned( Positioned(
top: 4, top: 4,
right: 4, right: 4,
@ -366,4 +400,4 @@ class _ProductBlockState extends State<ProductBlock> {
) )
); );
} }
} }

View File

@ -1,7 +1,8 @@
import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/screens/home_screen.dart'; // Importing HomePage screen
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart'; // Importing custom background widget
import 'package:flutter/material.dart'; import 'package:flutter/material.dart'; // Flutter material package for UI components
// Stateful widget for the Attendance Success screen
class AttendanceSuccess extends StatefulWidget { class AttendanceSuccess extends StatefulWidget {
const AttendanceSuccess({super.key}); const AttendanceSuccess({super.key});
@ -9,10 +10,13 @@ class AttendanceSuccess extends StatefulWidget {
State<AttendanceSuccess> createState() => _VerifySuccessFullScreenState(); State<AttendanceSuccess> createState() => _VerifySuccessFullScreenState();
} }
// State implementation for AttendanceSuccess screen
class _VerifySuccessFullScreenState extends State<AttendanceSuccess> { class _VerifySuccessFullScreenState extends State<AttendanceSuccess> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Delayed navigation to HomePage after 2 seconds
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement( Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage())); context, MaterialPageRoute(builder: (context) => const HomePage()));
@ -22,19 +26,22 @@ class _VerifySuccessFullScreenState extends State<AttendanceSuccess> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
// Using CommonBackground widget for consistent styling
body: CommonBackground( body: CommonBackground(
child: Center( child: Center(
// Centering content
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, // Vertically center elements
children: [ children: [
// Displaying confirmation message with styling
Container( Container(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF243B8A), // Background color of the message box color: const Color(0xFF243B8A), // Message box background color
borderRadius: BorderRadius.circular(15.0), borderRadius: BorderRadius.circular(15.0), // Rounded corners for the message box
), ),
child: const Text( child: const Text(
'Your Attendance\nHas been\nMarked!', 'Your Attendance\nHas been\nMarked!', // Text displayed in the center of the screen
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 36, fontSize: 36,
@ -44,8 +51,8 @@ class _VerifySuccessFullScreenState extends State<AttendanceSuccess> {
), ),
), ),
), ),
const SizedBox(height: 20), // Add some space between the text and the image const SizedBox(height: 20), // Space between the text and image (image currently commented out)
// Image.asset('assets/check_circle.png'), // Image.asset('assets/check_circle.png'), // Image for success status (currently commented out)
], ],
), ),
), ),

View File

@ -17,68 +17,73 @@ class UpdateInventoryTaskScreen extends StatefulWidget {
} }
class _UpdateInventoryTaskScreenState extends State<UpdateInventoryTaskScreen> { class _UpdateInventoryTaskScreenState extends State<UpdateInventoryTaskScreen> {
late DailyTaskProvider _dailyTaskProvider; late DailyTaskProvider _dailyTaskProvider; // Provider to manage daily tasks
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_dailyTaskProvider = DailyTaskProvider(); _dailyTaskProvider = DailyTaskProvider(); // Initialize the provider
apiCall(); apiCall(); // Fetch tasks from the API
} }
// Function to fetch new and pending tasks
void apiCall() async { void apiCall() async {
await _dailyTaskProvider.getTask(type: 'New',isAddPending: true); await _dailyTaskProvider.getTask(type: 'New', isAddPending: true); // Get new tasks
await _dailyTaskProvider.getTask(type: 'Pending',isAddPending: true); await _dailyTaskProvider.getTask(type: 'Pending', isAddPending: true); // Get pending tasks
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _dailyTaskProvider, create: (context) => _dailyTaskProvider, // Provide the DailyTaskProvider to the widget tree
child: Scaffold( child: Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true, // Extend the body behind the app bar
appBar: _buildAppBar(), appBar: _buildAppBar(), // Build the app bar
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Navigation drawer
body: CommonBackground( body: CommonBackground(
child: SafeArea( child: SafeArea(
child: _buildTaskList(), child: _buildTaskList(), // Build the list of tasks
), ),
), ),
), ),
); );
} }
// Function to build the app bar
CommonAppBar _buildAppBar() { CommonAppBar _buildAppBar() {
return CommonAppBar( return CommonAppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Transparent background for app bar
elevation: 0, elevation: 0, // No shadow for app bar
actions: [ actions: [
// Back button in the app bar
IconButton( IconButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context), // Navigate back
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
title: const Text( title: const Text(
'Inventory Update Tasks', '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() { Widget _buildTaskList() {
return Consumer<DailyTaskProvider>( return Consumer<DailyTaskProvider>(
builder: (context, value, child) { builder: (context, value, child) {
// Show loading indicator while fetching tasks
if (value.isLoading) { if (value.isLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
// Filter tasks that require inventory updates
final inventoryTasks = value.newTasksList final inventoryTasks = value.newTasksList
.where((task) => task.task?.toLowerCase() == 'update inventory data') .where((task) => task.task?.toLowerCase() == 'update inventory data')
.toList(); .toList();
// Show message if there are no inventory tasks
if (inventoryTasks.isEmpty) { if (inventoryTasks.isEmpty) {
return const Center( return const Center(
child: Text( child: Text(
@ -92,56 +97,64 @@ class _UpdateInventoryTaskScreenState extends State<UpdateInventoryTaskScreen> {
); );
} }
// Build the list view for inventory tasks
return ListView.separated( return ListView.separated(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: inventoryTasks.length, itemCount: inventoryTasks.length, // Number of tasks to display
separatorBuilder: (context, index) => const SizedBox(height: 8), separatorBuilder: (context, index) => const SizedBox(height: 8), // Space between items
itemBuilder: (context, index) => _buildTaskCard(inventoryTasks[index]), itemBuilder: (context, index) => _buildTaskCard(inventoryTasks[index]), // Build each task card
); );
}, },
); );
} }
// Function to build an individual task card
Widget _buildTaskCard(Tasks tasksList) { Widget _buildTaskCard(Tasks tasksList) {
return InkWell( return InkWell(
onTap: () { onTap: () {
// Navigate to AddProductsScreen when a task card is tapped
if (tasksList.sId != null && tasksList.addedFor != null) { if (tasksList.sId != null && tasksList.addedFor != null) {
Navigator.push( Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddProductsScreen( builder: (context) => AddProductsScreen(
distributorType: tasksList.addedFor!, distributorType: tasksList.addedFor!, // Pass distributor type
inventoryId: tasksList.sId!, inventoryId: tasksList.sId!, // Pass inventory ID
tradeName: tasksList.tradeName??'', tradeName: tasksList.tradeName ?? '', // Pass trader name
pdRdId: tasksList.sId!))); pdRdId: tasksList.sId!, // Pass PD/RD ID
),
),
);
} }
}, },
child: Card( child: Card(
color: Colors.white, color: Colors.white, // Card color
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), // Card shape
child: ListTile( child: ListTile(
leading: const Icon(Icons.inventory, color: Colors.blueAccent), leading: const Icon(Icons.inventory, color: Colors.blueAccent), // Leading icon
title: Text( title: Text(
tasksList.task ?? '', tasksList.task ?? '', // Task name
style: const TextStyle( style: const TextStyle(
color: Colors.black87, color: Colors.black87,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 16, fontSize: 16,
fontFamily: 'Anek', fontFamily: 'Anek', // Font style
), ),
), ),
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align subtitles to the start
children: [ children: [
Text('Distributor: ${tasksList.addedFor ?? ""}'), Text('Distributor: ${tasksList.addedFor ?? ""}'), // Display distributor name
Text('Trader Name: ${tasksList.tradeName??''}'), Text('Trader Name: ${tasksList.tradeName ?? ''}'), // Display trader name
if(tasksList.taskDueDate != null) Text('Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), // Display due date if it exists
Text('Priority: ${tasksList.taskPriority}'), 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
), ),
), ),
); );
} }
} }

View File

@ -10,12 +10,14 @@ import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AddSalesProductScreen extends StatefulWidget { class AddSalesProductScreen extends StatefulWidget {
// Final properties to store data passed from previous screen
final String distributorType; final String distributorType;
final String tradeName; final String tradeName;
final String pdRdId; final String pdRdId;
final String? inventoryId; final String? inventoryId;
const AddSalesProductScreen({ const AddSalesProductScreen({
super.key, super.key,
required this.distributorType, required this.distributorType,
required this.tradeName, required this.tradeName,
@ -28,6 +30,7 @@ class AddSalesProductScreen extends StatefulWidget {
} }
class _AddSalesProductScreenState extends State<AddSalesProductScreen> { class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
// Controllers for text fields
final searchController = TextEditingController(); final searchController = TextEditingController();
late AddSalesProvider salesTaskProvider; late AddSalesProvider salesTaskProvider;
final dateController = TextEditingController(); final dateController = TextEditingController();
@ -36,6 +39,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Initialize the provider and set the current date
salesTaskProvider = Provider.of<AddSalesProvider>(context, listen: false); salesTaskProvider = Provider.of<AddSalesProvider>(context, listen: false);
dateController.text = DateFormat('dd/MM/yyyy').format(DateTime.now()); dateController.text = DateFormat('dd/MM/yyyy').format(DateTime.now());
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -45,6 +49,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
@override @override
void dispose() { void dispose() {
// Dispose of the text controllers and reset the provider when the widget is removed
searchController.dispose(); searchController.dispose();
dateController.dispose(); dateController.dispose();
if (mounted) { if (mounted) {
@ -57,10 +62,10 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopScope( return PopScope(
canPop: true, canPop: true,
child: CommonBackground( child: CommonBackground(// Common background for the screen
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(// Custom AppBar with back button and title
actions: [ actions: [
IconButton( IconButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
@ -94,7 +99,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [// Floating button to add products
FloatingActionButton.extended( FloatingActionButton.extended(
onPressed: () => _showProductSelectionBottomSheet(context), onPressed: () => _showProductSelectionBottomSheet(context),
backgroundColor: Colors.white, backgroundColor: Colors.white,
@ -103,6 +108,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
), ),
if (value.selectedProducts.isNotEmpty) ...[ if (value.selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0), const SizedBox(height: 16.0),
// Submit button that appears after products are selected
CommonElevatedButton( CommonElevatedButton(
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
@ -119,7 +125,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
], ],
), ),
), ),
body: Consumer<AddSalesProvider>( body: Consumer<AddSalesProvider>(// Displays the list of selected products or loading indicator
builder: (context, value, child) { builder: (context, value, child) {
return Stack( return Stack(
children: [ children: [
@ -127,7 +133,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: TextFormField( child: TextFormField(// Date selection field
controller: dateController, controller: dateController,
readOnly: true, readOnly: true,
decoration: const InputDecoration( decoration: const InputDecoration(
@ -139,6 +145,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
), ),
), ),
), ),
// Display selected products list
if (value.selectedProducts.isNotEmpty) if (value.selectedProducts.isNotEmpty)
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
@ -162,6 +169,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
), ),
], ],
), ),
// Loading indicator
if (value.isLoading) if (value.isLoading)
Container( Container(
color: Colors.black12, color: Colors.black12,
@ -175,7 +183,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
), ),
); );
} }
// Function to show product selection modal bottom sheet
void _showProductSelectionBottomSheet(BuildContext context) { void _showProductSelectionBottomSheet(BuildContext context) {
showModalBottomSheet( showModalBottomSheet(
isScrollControlled: true, isScrollControlled: true,
@ -195,12 +203,13 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search),
), ),
onChanged: (val) { onChanged: (val) {// Filter product list based on search query
value.filterProducts(val); value.filterProducts(val);
setState(() {}); setState(() {});
}, },
), ),
), ),
// List of products to choose from
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: searchController.text.isEmpty itemCount: searchController.text.isEmpty
@ -239,9 +248,9 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
), ),
); );
}, },
).whenComplete(() => setState(() {})); ).whenComplete(() => setState(() {}));// Refresh UI after modal is closed
} }
// Function to submit selected products
void _submitProducts(AddSalesProvider value) { void _submitProducts(AddSalesProvider value) {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
if (value.selectedProducts.isNotEmpty && if (value.selectedProducts.isNotEmpty &&
@ -257,7 +266,7 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
date: dateController.text.trim(), date: dateController.text.trim(),
tradeName: widget.tradeName, tradeName: widget.tradeName,
); );
} else { } else { // Show error message if any product information is missing
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Please fill out all product details, including sale and inventory.'), content: Text('Please fill out all product details, including sale and inventory.'),

View File

@ -1,42 +1,42 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart'; import 'package:table_calendar/table_calendar.dart';
import '../widgets/common_app_bar.dart'; import '../widgets/common_app_bar.dart'; // Custom AppBar widget
import '../widgets/common_background.dart'; import '../widgets/common_background.dart'; // Custom background widget
import '../widgets/common_drawer.dart'; 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(); // Main Calendar screen
DateTime? _selectedDay=DateTime.now();
class CalendarScreen extends StatelessWidget { class CalendarScreen extends StatelessWidget {
const CalendarScreen({super.key}); const CalendarScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( return CommonBackground( // Wrapper for common background styling
child: Scaffold( child: Scaffold(
appBar: CommonAppBar( appBar: CommonAppBar( // Custom AppBar
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Go back to the previous screen
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
title: const Text('Calendar'), title: const Text('Calendar'), // Title of the AppBar
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Transparent background for the AppBar
elevation: 0, elevation: 0,
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Transparent background for the Scaffold
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Custom drawer widget
body: const SingleChildScrollView( body: const SingleChildScrollView( // Scrollable view to accommodate the calendar and events list
child: Column( child: Column(
children: [ children: [
CalendarWidget(), CalendarWidget(), // Calendar widget to show calendar dates
EventsList(), 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 { class CalendarWidget extends StatefulWidget {
const CalendarWidget({super.key}); const CalendarWidget({super.key});
@ -55,37 +56,37 @@ class CalendarWidget extends StatefulWidget {
class _CalendarWidgetState extends State<CalendarWidget> { class _CalendarWidgetState extends State<CalendarWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card( // Card UI to display the calendar inside
margin: const EdgeInsets.all(16), margin: const EdgeInsets.all(16), // Margin around the card
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16), // Padding inside the card
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left
children: [ children: [
const Text( const Text(
'Calendar', 'Calendar',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // Calendar heading
), ),
const Text( const Text(
'Month/Year', '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), const SizedBox(height: 10),
TableCalendar( TableCalendar( // TableCalendar widget to show date selection
firstDay: DateTime.utc(1900, 5, 1), firstDay: DateTime.utc(1900, 5, 1), // Earliest selectable date
lastDay: DateTime.utc(2900, 5, 1), lastDay: DateTime.utc(2900, 5, 1), // Latest selectable date
focusedDay: _focusedDay, focusedDay: _focusedDay, // Currently focused day
selectedDayPredicate: (day) { selectedDayPredicate: (day) {
return isSameDay(_selectedDay, day); return isSameDay(_selectedDay, day); // Highlight selected day
}, },
onDaySelected: (selectedDay, focusedDay) { onDaySelected: (selectedDay, focusedDay) {
setState(() { setState(() { // Update selected and focused days
_selectedDay = selectedDay; _selectedDay = selectedDay;
_focusedDay = focusedDay; _focusedDay = focusedDay;
}); });
}, },
onPageChanged: (focusedDay) { onPageChanged: (focusedDay) {
_focusedDay = focusedDay; _focusedDay = focusedDay; // Change focused day when calendar page changes
}, },
), ),
], ],
@ -95,23 +96,25 @@ class _CalendarWidgetState extends State<CalendarWidget> {
} }
} }
// Widget to display a list of events
class EventsList extends StatelessWidget { class EventsList extends StatelessWidget {
const EventsList({super.key}); const EventsList({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Card( return const Card( // Card UI for the event list
margin: EdgeInsets.all(16), margin: EdgeInsets.all(16), // Margin around the card
child: Padding( child: Padding(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16), // Padding inside the card
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left
children: [ children: [
Text( Text(
'Events List', 'Events List', // Heading for the events list
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
SizedBox(height: 10), SizedBox(height: 10),
// Display individual event items
EventItem( EventItem(
date: '10-06-2024', date: '10-06-2024',
event: 'Meeting with Territory Manager', event: 'Meeting with Territory Manager',
@ -128,41 +131,42 @@ class EventsList extends StatelessWidget {
} }
} }
// Widget for displaying individual event items
class EventItem extends StatelessWidget { class EventItem extends StatelessWidget {
final String date; final String date; // Event date
final String event; final String event; // Event description
const EventItem({super.key, required this.date, required this.event}); const EventItem({super.key, required this.date, required this.event});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12), // Padding inside the event container
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue[50], color: Colors.blue[50], // Light blue background for the event item
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8), // Rounded corners for the container
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left
children: [ children: [
Text('Date: $date'), Text('Date: $date'), // Display event date
Text( Text(
'Event: $event', 'Event: $event', // Display event description
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
ElevatedButton( ElevatedButton( // Button to view event details
onPressed: () { onPressed: () {
// Add view details functionality here // Add view details functionality here
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, foregroundColor: Colors.white, // Text color
backgroundColor: Colors.blue[800], backgroundColor: Colors.blue[800], // Button background color
), ),
child: const Text('VIEW DETAILS'), child: const Text('VIEW DETAILS'), // Button label
), ),
], ],
), ),
); );
} }
} }

View File

@ -7,7 +7,6 @@ import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:cheminova/widgets/common_text_form_field.dart'; import 'package:cheminova/widgets/common_text_form_field.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../widgets/common_drawer.dart'; import '../widgets/common_drawer.dart';
import 'home_screen.dart'; import 'home_screen.dart';
@ -19,150 +18,155 @@ class ChangePasswordPage extends StatefulWidget {
} }
class _ChangePasswordPageState extends State<ChangePasswordPage> { class _ChangePasswordPageState extends State<ChangePasswordPage> {
late ChangePasswordProvider changePasswordProvider; late ChangePasswordProvider changePasswordProvider; // Provider to manage state and logic
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>(); // Form key to manage form validation
@override @override
void initState() { void initState() {
changePasswordProvider = ChangePasswordProvider(); changePasswordProvider = ChangePasswordProvider(); // Initialize the provider
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Use ChangeNotifierProvider to listen to ChangePasswordProvider updates
return ChangeNotifierProvider<ChangePasswordProvider>( return ChangeNotifierProvider<ChangePasswordProvider>(
create: (_) => changePasswordProvider, create: (_) => changePasswordProvider,
builder: (context, child) { builder: (context, child) {
return Stack( return Stack( // Stack to manage overlay loading indicator
children: [ children: [
CommonBackground( CommonBackground( // Custom background widget
isFullWidth: true, isFullWidth: true,
child: Scaffold( child: Scaffold(
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Custom drawer widget
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
title: const Text(''), title: const Text(''),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Transparent AppBar
elevation: 0, elevation: 0,
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Back button action
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
), ),
body: SingleChildScrollView( body: SingleChildScrollView( // Scrollable form
child: Column( child: Column(
children: [ children: [
// const SizedBox(height: 50),
// const SizedBox(height: 60),
Container( Container(
padding: const EdgeInsets.all(20.0) padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
.copyWith(top: 15, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 30.0), margin: const EdgeInsets.symmetric(horizontal: 30.0),
decoration: BoxDecoration( decoration: BoxDecoration( // Styling for the form container
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0)), borderRadius: BorderRadius.circular(26.0)),
child: Form( child: Form( // Form widget to validate input fields
key: _formKey, key: _formKey, // Reference to form state
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const SizedBox(height: 20), const SizedBox(height: 20),
const Text('Change Password', const Text(
style: TextStyle( 'Change Password',
fontSize: 30, style: TextStyle(
color: Colors.black, fontSize: 30,
fontWeight: FontWeight.w500, color: Colors.black,
fontFamily: 'Roboto')), fontWeight: FontWeight.w500,
fontFamily: 'Roboto',
),
),
const SizedBox(height: 20), const SizedBox(height: 20),
// Old Password Field
Consumer<ChangePasswordProvider>( Consumer<ChangePasswordProvider>(
builder: (context, value, child) => builder: (context, value, child) =>
CommonTextFormField( CommonTextFormField(
controller: value.oldPasswordController, controller: value.oldPasswordController, // Old password controller
validator: (val) { validator: (val) {
if (val == null || val.isEmpty) { if (val == null || val.isEmpty) {
return 'Please enter your password'; return 'Please enter your password'; // Validation for empty input
} }
return null; return null;
}, },
title: 'Old Password'), title: 'Old Password'),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// New Password Field
Consumer<ChangePasswordProvider>( Consumer<ChangePasswordProvider>(
builder: (context, value, child) => builder: (context, value, child) =>
CommonTextFormField( CommonTextFormField(
controller: value.newPasswordController, controller: value.newPasswordController, // New password controller
validator: (val) { validator: (val) {
if (val == null || val.isEmpty) { if (val == null || val.isEmpty) {
return 'Please enter your password'; return 'Please enter your password'; // Validation for empty input
} }
return null; return null;
}, },
title: 'New Password'), title: 'New Password'),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Confirm Password Field
Consumer<ChangePasswordProvider>( Consumer<ChangePasswordProvider>(
builder: (context, value, child) => builder: (context, value, child) =>
CommonTextFormField( CommonTextFormField(
controller: controller: value.confirmPasswordController, // Confirm password controller
value.confirmPasswordController,
validator: (val) { validator: (val) {
if (val == null || val.isEmpty) { if (val == null || val.isEmpty) {
return 'Please enter your password'; return 'Please enter your password'; // Validation for empty input
} else if (val != } else if (val !=
value value.newPasswordController.text) {
.newPasswordController.text) { return 'Password does not match'; // Validation for mismatched password
return 'Password does not match';
} }
return null; return null;
}, },
title: 'Confirm Password'), title: 'Confirm Password'),
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
// Submit button
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Consumer<ChangePasswordProvider>( child: Consumer<ChangePasswordProvider>(
builder: (context, value, child) => builder: (context, value, child) =>
CommonElevatedButton( CommonElevatedButton(
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791), // Button styling
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'SIGN IN', text: 'SIGN IN', // Button text
isLoading: false, isLoading: false,
onPressed: value.isLoading onPressed: value.isLoading
? null ? null
: () async { : () async {
if (_formKey.currentState! if (_formKey.currentState!.validate()) {
.validate()) { // If form is valid, proceed with password change
value value.changePassword().then((result) {
.changePassword() var (status, message) = result;
.then((result) { ScaffoldMessenger.of(context).showSnackBar(
var (status, message) = SnackBar(content: Text(message))
result; ); // Show success or error message
ScaffoldMessenger.of(context) if (status) {
.showSnackBar(SnackBar( // Navigate to HomePage if password change is successful
content: Navigator.pushReplacement(
Text(message))); context,
if (status) { MaterialPageRoute(
Navigator.pushReplacement( builder: (context) =>
context, const HomePage()));
MaterialPageRoute(
builder: (context) =>
const HomePage()));
}
});
} }
}, });
), }
)) },
),
),
)
], ],
), ),
), ),
@ -171,12 +175,15 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
), ),
)), )),
), ),
Consumer<ChangePasswordProvider>(
builder: (context, value, child) => value.isLoading? // Overlay loading indicator when processing
Container( Consumer<ChangePasswordProvider>(
color: Colors.black.withOpacity(0.5), builder: (context, value, child) => value.isLoading
child: const Center(child: CircularProgressIndicator())):Container(), ? Container(
) color: Colors.black.withOpacity(0.5), // Overlay background
child: const Center(child: CircularProgressIndicator())) // Show loading indicator
: Container(),
)
], ],
); );
}); });

View File

@ -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/provider/collect_kyc_provider.dart';
import 'package:cheminova/screens/retailer_details_screen.dart'; import 'package:cheminova/screens/retailer_details_screen.dart';
import 'package:cheminova/screens/upload_documents_screen.dart'; import 'package:cheminova/screens/upload_documents_screen.dart';
import 'package:cheminova/screens/verification_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:cheminova/widgets/common_drawer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cheminova/widgets/common_background.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'; import '../widgets/common_app_bar.dart';
class CollectKycScreen extends StatefulWidget { class CollectKycScreen extends StatefulWidget {
final String id; final String id; // ID passed as a required parameter
const CollectKycScreen({super.key, required this.id}); const CollectKycScreen({super.key, required this.id});
@override @override
@ -20,22 +20,23 @@ class CollectKycScreen extends StatefulWidget {
class CollectKycScreenState extends State<CollectKycScreen> class CollectKycScreenState extends State<CollectKycScreen>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late CollectKycProvider collectKycProvider; late CollectKycProvider collectKycProvider; // Provider to handle business logic and state
final _pages = [ final _pages = [
const RetailerDetailsScreen(), const RetailerDetailsScreen(), // First tab for Retailer Details
const UploadDocumentScreen(), const UploadDocumentScreen(), // Second tab for uploading documents
const VerifyDocumentsScreen() const VerifyDocumentsScreen() // Third tab for verifying documents
]; ];
@override @override
void initState() { void initState() {
collectKycProvider = CollectKycProvider(); collectKycProvider = CollectKycProvider(); // Initialize the provider
collectKycProvider.setId(widget.id); collectKycProvider.setId(widget.id); // Set the retailer ID in the provider
collectKycProvider.tabController = TabController(length: 3, vsync: this); collectKycProvider.tabController = TabController(length: 3, vsync: this); // Initialize TabController for 3 tabs
super.initState(); super.initState();
} }
// Controllers for form fields
final locationController = TextEditingController(); final locationController = TextEditingController();
final notesController = TextEditingController(); final notesController = TextEditingController();
final dealerController = TextEditingController(); final dealerController = TextEditingController();
@ -48,38 +49,42 @@ class CollectKycScreenState extends State<CollectKycScreen>
final aadharcardController = TextEditingController(); final aadharcardController = TextEditingController();
final pancardController = TextEditingController(); final pancardController = TextEditingController();
// File variables for image picking
File? _selfieImage; File? _selfieImage;
File? _pesticideLicenseImage; File? _pesticideLicenseImage;
File? _fertilizerLicenseImage; File? _fertilizerLicenseImage;
final ImagePicker _picker = ImagePicker(); final ImagePicker _picker = ImagePicker(); // Image picker instance
// Function to pick image for selfie or documents
Future<void> _pickImage(ImageSource source, bool isSelfie) async { Future<void> _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(() { setState(() {
if (pickedFile != null) { if (pickedFile != null) {
if (isSelfie) { 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<void> _pickLicenseImage(ImageSource source, bool isPesticide) async { Future<void> _pickLicenseImage(ImageSource source, bool isPesticide) async {
final pickedFile = await _picker.pickImage(source: source); final pickedFile = await _picker.pickImage(source: source);
setState(() { setState(() {
if (pickedFile != null) { if (pickedFile != null) {
if (isPesticide) { if (isPesticide) {
_pesticideLicenseImage = File(pickedFile.path); _pesticideLicenseImage = File(pickedFile.path); // Set the pesticide license image
} else { } 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) { void _showPicker(BuildContext context, bool isSelfie) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -91,14 +96,14 @@ class CollectKycScreenState extends State<CollectKycScreen>
leading: const Icon(Icons.photo_library), leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'), title: const Text('Photo Library'),
onTap: () { onTap: () {
_pickImage(ImageSource.gallery, isSelfie); _pickImage(ImageSource.gallery, isSelfie); // Pick image from gallery
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
ListTile( ListTile(
leading: const Icon(Icons.photo_camera), leading: const Icon(Icons.photo_camera),
title: const Text('Camera'), title: const Text('Camera'),
onTap: () { onTap: () {
_pickImage(ImageSource.camera, isSelfie); _pickImage(ImageSource.camera, isSelfie); // Pick image from camera
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
@ -108,6 +113,7 @@ class CollectKycScreenState extends State<CollectKycScreen>
}); });
} }
// Function to show picker for licenses
void _showLicensePicker(BuildContext context, bool isPesticide) { void _showLicensePicker(BuildContext context, bool isPesticide) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -119,14 +125,14 @@ class CollectKycScreenState extends State<CollectKycScreen>
leading: const Icon(Icons.photo_library), leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'), title: const Text('Photo Library'),
onTap: () { onTap: () {
_pickLicenseImage(ImageSource.gallery, isPesticide); _pickLicenseImage(ImageSource.gallery, isPesticide); // Pick image for license from gallery
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
ListTile( ListTile(
leading: const Icon(Icons.photo_camera), leading: const Icon(Icons.photo_camera),
title: const Text('Camera'), title: const Text('Camera'),
onTap: () { onTap: () {
_pickLicenseImage(ImageSource.camera, isPesticide); _pickLicenseImage(ImageSource.camera, isPesticide); // Pick image for license from camera
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
@ -139,63 +145,64 @@ class CollectKycScreenState extends State<CollectKycScreen>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => collectKycProvider, create: (context) => collectKycProvider, // Provide the state management
child: Consumer<CollectKycProvider>( child: Consumer<CollectKycProvider>(
builder: (BuildContext context, value, Widget? child) => builder: (BuildContext context, value, Widget? child) =>
Stack(children: [ Stack(children: [
CommonBackground( CommonBackground(
child: DefaultTabController( child: DefaultTabController(
length: 3, length: 3, // Number of tabs
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(100), preferredSize: const Size.fromHeight(100), // Custom AppBar with Tabs
child: CommonAppBar( child: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Back button to navigate back
}, },
icon: icon: Image.asset('assets/Back_attendance.png'),
Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20),
padding: const EdgeInsets.only(right: 20), ),
), ],
], title: const Text('Collect KYC Data', // Title for the AppBar
title: const Text('Collect KYC Data', style: TextStyle(
style: TextStyle( fontSize: 20,
fontSize: 20, color: Colors.black,
color: Colors.black, fontWeight: FontWeight.w400,
fontWeight: FontWeight.w400, fontFamily: 'Anek')),
fontFamily: 'Anek')), backgroundColor: Colors.transparent,
backgroundColor: Colors.transparent, elevation: 0,
elevation: 0, bottom: TabBar( // TabBar for navigation between the pages
bottom: TabBar( controller: value.tabController, // Use the TabController from provider
controller: value.tabController, padding: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.symmetric( dividerColor: Colors.transparent,
horizontal: 10), indicatorColor: Colors.yellow,
dividerColor: Colors.transparent, labelColor: Colors.white,
indicatorColor: Colors.yellow, unselectedLabelColor: Colors.black,
labelColor: Colors.white, indicatorSize: TabBarIndicatorSize.tab,
unselectedLabelColor: Colors.black, indicator: BoxDecoration(
indicatorSize: TabBarIndicatorSize.tab, color: Colors.blue,
indicator: BoxDecoration( borderRadius: BorderRadius.circular(10)), // Custom styling for selected tab
color: Colors.blue, tabs: const [
borderRadius: BorderRadius.circular(10)), Tab(text: 'Details'), // First tab
tabs: const [ Tab(text: 'Documents'), // Second tab
Tab(text: 'Details'), Tab(text: 'Verify') // Third tab
Tab(text: 'Documents'), ]))),
Tab(text: 'Verify')
]))), drawer: const CommonDrawer(), // Drawer for navigation
drawer: const CommonDrawer(), body: TabBarView( // View for each tab
body: TabBarView( controller: value.tabController,
controller: value.tabController, physics: const NeverScrollableScrollPhysics(), // Disable swipe gesture for tab switch
physics: const NeverScrollableScrollPhysics(), children: _pages)))),
children: _pages)))),
if (value.isLoading) // Loading indicator overlay when `isLoading` is true
Container( if (value.isLoading)
color: Colors.black.withOpacity(0.5), Container(
child: const Center(child: CircularProgressIndicator())) color: Colors.black.withOpacity(0.5), // Semi-transparent overlay
]), child: const Center(child: CircularProgressIndicator())) // Show loading spinner
]),
), ),
); );
} }

View File

@ -12,6 +12,8 @@ import 'package:provider/provider.dart';
import '../provider/daily_task_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 { class DailyTasksScreen extends StatefulWidget {
const DailyTasksScreen({super.key}); const DailyTasksScreen({super.key});
@ -20,10 +22,11 @@ class DailyTasksScreen extends StatefulWidget {
} }
class _DailyTasksScreenState extends State<DailyTasksScreen> { class _DailyTasksScreenState extends State<DailyTasksScreen> {
final List<String> _tabTitles = ['NEW', 'PENDING', 'COMPLETED']; final List<String> _tabTitles = ['NEW', 'PENDING', 'COMPLETED']; // Tab labels
int _selectedTabIndex = 0; int _selectedTabIndex = 0; // Current selected tab index
late DailyTaskProvider _dailyTaskProvider; late DailyTaskProvider _dailyTaskProvider;
/// Initial setup for getting the initial data (New tasks).
@override @override
void initState() { void initState() {
_dailyTaskProvider = DailyTaskProvider(); _dailyTaskProvider = DailyTaskProvider();
@ -31,6 +34,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
super.initState(); super.initState();
} }
/// The build function returns the widget tree for the DailyTasksScreen.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
@ -44,9 +48,9 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildCustomTabBar(), _buildCustomTabBar(), // Custom tab bar for task categories
Expanded( Expanded(
child: _buildTaskList(_selectedTabIndex), child: _buildTaskList(_selectedTabIndex), // Task list view based on selected tab
), ),
], ],
), ),
@ -55,6 +59,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
)); ));
} }
/// Builds the app bar with a back button and the title 'Daily Tasks'.
CommonAppBar _buildAppBar() { CommonAppBar _buildAppBar() {
return CommonAppBar( return CommonAppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -73,6 +78,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
); );
} }
/// Custom tab bar at the top of the screen to switch between task categories.
Widget _buildCustomTabBar() { Widget _buildCustomTabBar() {
return Container( return Container(
height: 60, height: 60,
@ -82,6 +88,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
children: List.generate(_tabTitles.length, (index) { children: List.generate(_tabTitles.length, (index) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
// Update selected tab index and fetch relevant task data
setState(() => _selectedTabIndex = index); setState(() => _selectedTabIndex = index);
if (index == 0) { if (index == 0) {
_dailyTaskProvider.getTask(type: 'New'); _dailyTaskProvider.getTask(type: 'New');
@ -96,10 +103,10 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: _selectedTabIndex == index gradient: _selectedTabIndex == index
? const LinearGradient( ? const LinearGradient(
colors: [Color(0xff004791), Color(0xff1a73e8)], colors: [Color(0xff004791), Color(0xff1a73e8)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
) )
: null, : null,
color: _selectedTabIndex == index color: _selectedTabIndex == index
? Colors.transparent ? Colors.transparent
@ -144,6 +151,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
); );
} }
/// Provides appropriate icons for the tabs.
IconData _getTabIcon(int index) { IconData _getTabIcon(int index) {
switch (index) { switch (index) {
case 0: case 0:
@ -157,78 +165,81 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
} }
} }
/// Builds the task list according to the selected tab.
Widget _buildTaskList(int tabIndex) { Widget _buildTaskList(int tabIndex) {
return Consumer<DailyTaskProvider>( return Consumer<DailyTaskProvider>(
builder: (context, value, child) => value.isLoading builder: (context, value, child) => value.isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: ListView.separated( : ListView.separated(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: _selectedTabIndex == 0 itemCount: _selectedTabIndex == 0
? value.newTasksList.length ? value.newTasksList.length
: _selectedTabIndex == 1 : _selectedTabIndex == 1
? value.pendingTasksList.length ? value.pendingTasksList.length
: value.completedTasksList.length, : value.completedTasksList.length,
separatorBuilder: (context, index) => const SizedBox(height: 8), separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final tasksList = tabIndex == 0 final tasksList = tabIndex == 0
? value.newTasksList ? value.newTasksList
: tabIndex == 1 : tabIndex == 1
? value.pendingTasksList ? value.pendingTasksList
: value.completedTasksList; : value.completedTasksList;
return _buildTaskCard(tasksList[index]); return _buildTaskCard(tasksList[index]);
}, },
), ),
); );
} }
/// Builds a task card widget based on the task details.
Widget _buildTaskCard(Tasks tasksList) { Widget _buildTaskCard(Tasks tasksList) {
return InkWell( return InkWell(
onTap: _selectedTabIndex == 2 onTap: _selectedTabIndex == 2
? null // Disable click when on the "COMPLETED" tab ? null // Disable click when on the "COMPLETED" tab
: () { : () {
print('Task: ${tasksList.toJson()}'); print('Task: ${tasksList.toJson()}');
if (tasksList.task == 'Collect KYC') { // Navigation to respective screens based on task type
Navigator.push( if (tasksList.task == 'Collect KYC') {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => MaterialPageRoute(
CollectKycScreen(id: tasksList.sId ?? ''))); builder: (context) =>
} else if (tasksList.task == 'REUPLOAD') { CollectKycScreen(id: tasksList.sId ?? '')));
Navigator.push( } else if (tasksList.task == 'REUPLOAD') {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => MaterialPageRoute(
CollectKycScreen(id: tasksList.taskId ?? ''))); builder: (context) =>
} else if (tasksList.task == 'Update Inventory Data') { CollectKycScreen(id: tasksList.taskId ?? '')));
Navigator.push( } else if (tasksList.task == 'Update Inventory Data') {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => AddProductsScreen( MaterialPageRoute(
distributorType: tasksList.addedFor!, builder: (context) => AddProductsScreen(
tradeName: tasksList.tradeName ?? '', distributorType: tasksList.addedFor!,
pdRdId: tasksList.addedForId!, tradeName: tasksList.tradeName ?? '',
inventoryId: tasksList.sId))); pdRdId: tasksList.addedForId!,
} else if (tasksList.task == 'Update Sales Data') { inventoryId: tasksList.sId)));
Navigator.push( } else if (tasksList.task == 'Update Sales Data') {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => AddSalesProductScreen( MaterialPageRoute(
distributorType: tasksList.addedFor!, builder: (context) => AddSalesProductScreen(
tradeName: tasksList.tradeName!, distributorType: tasksList.addedFor!,
pdRdId: tasksList.addedForId!, tradeName: tasksList.tradeName!,
inventoryId:tasksList.sId, pdRdId: tasksList.addedForId!,
))); inventoryId: tasksList.sId,
} else if (tasksList.task == 'Visit RD/PD') { )));
Navigator.push( } else if (tasksList.task == 'Visit RD/PD') {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => VisitDealersScreen( MaterialPageRoute(
tradeName: tasksList.tradeName ?? '', builder: (context) => VisitDealersScreen(
id: tasksList.sId, tradeName: tasksList.tradeName ?? '',
type: tasksList.addedFor, id: tasksList.sId,
))); type: tasksList.addedFor,
} )));
}, }
},
child: Card( child: Card(
color: Colors.white, color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),

View File

@ -1,7 +1,8 @@
import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/screens/home_screen.dart'; // Import the home screen for navigation after submission
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart'; // Import custom background widget
import 'package:flutter/material.dart'; import 'package:flutter/material.dart'; // Import Flutter material package
// Create a StatefulWidget for the success screen after data submission
class DataSubmitSuccessFullScreen extends StatefulWidget { class DataSubmitSuccessFullScreen extends StatefulWidget {
const DataSubmitSuccessFullScreen({super.key}); const DataSubmitSuccessFullScreen({super.key});
@ -9,30 +10,41 @@ class DataSubmitSuccessFullScreen extends StatefulWidget {
State<DataSubmitSuccessFullScreen> createState() => DataSubmitSuccessFullScreenState(); State<DataSubmitSuccessFullScreen> createState() => DataSubmitSuccessFullScreenState();
} }
// Define the state class for the DataSubmitSuccessFullScreen
class DataSubmitSuccessFullScreenState extends State<DataSubmitSuccessFullScreen> { class DataSubmitSuccessFullScreenState extends State<DataSubmitSuccessFullScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Navigate to the HomePage after a 2-second delay
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement( Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage())); context, MaterialPageRoute(builder: (context) => const HomePage()));
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( // Build the UI for the success screen
body: CommonBackground( return Scaffold(
child: Center( body: CommonBackground( // Use the custom background widget
child: Center( // Center the content vertically and horizontally
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, // Center items in the column
children: [ children: [
// Display a success message
const Text('Data Submitted\nsuccessfully', const Text('Data Submitted\nsuccessfully',
style: TextStyle( style: TextStyle(
fontSize: 36, fontSize: 36, // Set font size
color: Colors.white, color: Colors.white, // Set font color
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400, // Set font weight
fontFamily: 'Anek')), fontFamily: 'Anek')), // Set font family
const SizedBox(height: 20), // Add some space between the text and the image 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
]
)
)
)
);
} }
} }

View File

@ -1,14 +1,15 @@
import 'package:cheminova/provider/forgot_password_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 'package:cheminova/screens/change_password_screen.dart'; // Import screen for changing password
import 'package:cheminova/screens/login_screen.dart'; import 'package:cheminova/screens/login_screen.dart'; // Import the login screen
import 'package:cheminova/screens/password_change_screen.dart'; import 'package:cheminova/screens/password_change_screen.dart'; // Import screen for password change
import 'package:cheminova/screens/verify_code_screen.dart'; import 'package:cheminova/screens/verify_code_screen.dart'; // Import screen for verifying code
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart'; // Import custom background widget
import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:cheminova/widgets/common_elevated_button.dart'; // Import custom elevated button widget
import 'package:cheminova/widgets/common_text_form_field.dart'; import 'package:cheminova/widgets/common_text_form_field.dart'; // Import custom text form field widget
import 'package:flutter/material.dart'; import 'package:flutter/material.dart'; // Import Flutter material package
import 'package:provider/provider.dart'; import 'package:provider/provider.dart'; // Import provider package for state management
// Create a StatefulWidget for the Forgot Password screen
class ForgotPasswordScreen extends StatefulWidget { class ForgotPasswordScreen extends StatefulWidget {
const ForgotPasswordScreen({super.key}); const ForgotPasswordScreen({super.key});
@ -16,29 +17,36 @@ class ForgotPasswordScreen extends StatefulWidget {
State<ForgotPasswordScreen> createState() => _ForgotPasswordScreenState(); State<ForgotPasswordScreen> createState() => _ForgotPasswordScreenState();
} }
// Define the state class for the ForgotPasswordScreen
class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> { class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>(); // Key to identify the form
late ForgotPasswordProvider forgotPasswordProvider; late ForgotPasswordProvider forgotPasswordProvider; // Provider for managing forgot password state
@override @override
void initState() { void initState() {
forgotPasswordProvider = ForgotPasswordProvider();
super.initState(); super.initState();
forgotPasswordProvider = ForgotPasswordProvider(); // Initialize the provider
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Use ChangeNotifierProvider to provide ForgotPasswordProvider to the widget tree
return ChangeNotifierProvider<ForgotPasswordProvider>( return ChangeNotifierProvider<ForgotPasswordProvider>(
create: (_) => forgotPasswordProvider, create: (_) => forgotPasswordProvider,
builder: (context, child) { builder: (context, child) {
return CommonBackground( return CommonBackground(
isFullWidth: false, isFullWidth: false,
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: AppBar(leading: InkWell(onTap:() { appBar: AppBar(
Navigator.pop(context); leading: InkWell(
},child: Image.asset('assets/Back_attendance.png')),backgroundColor: Colors.transparent,), onTap: () {
Navigator.pop(context); // Navigate back when the back button is pressed
},
child: Image.asset('assets/Back_attendance.png'),
),
backgroundColor: Colors.transparent,
),
body: Center( body: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Container( child: Container(
@ -50,19 +58,20 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
borderRadius: BorderRadius.circular(26.0), borderRadius: BorderRadius.circular(26.0),
), ),
child: Form( child: Form(
key: _formKey, key: _formKey, // Assign the form key
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// Logo displayed at the top
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Image.asset( child: Image.asset(
'assets/lock_logo2.png', 'assets/lock_logo2.png',
height: 50.0, // Adjust the height as needed height: 50.0, // Set height for the logo
width: 50.0, // Adjust the width as needed width: 50.0, // Set width for the logo
), ),
), ),
const Text( const Text(
'Forgot Password', 'Forgot Password',
@ -83,56 +92,62 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Text field for entering email
Consumer<ForgotPasswordProvider>( Consumer<ForgotPasswordProvider>(
builder: (context, value, child) => CommonTextFormField( builder: (context, value, child) => CommonTextFormField(
controller: value.emailController, controller: value.emailController, // Controller for the email input
title: ' Enter Your Email ID'), title: ' Enter Your Email ID',
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Button to navigate back to the login screen
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: TextButton( child: TextButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Navigate back to login
}, },
child: const Text('Back to Login', child: const Text(
style: TextStyle( 'Back to Login',
fontSize: 20, style: TextStyle(
color: Colors.black, fontSize: 20,
fontWeight: FontWeight.w400, color: Colors.black,
fontFamily: 'Roboto')), fontWeight: FontWeight.w400,
fontFamily: 'Roboto',
),
),
), ),
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
// Button to send the password reset request
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Consumer<ForgotPasswordProvider>( child: Consumer<ForgotPasswordProvider>(
builder: (context, value, child) => CommonElevatedButton( builder: (context, value, child) => CommonElevatedButton(
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791), // Button background color
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'SEND', text: 'SEND', // Button text
onPressed: value.isLoading onPressed: value.isLoading // Disable button if loading
? null ? null
: () async { : () 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) { value.forgotPassword().then((result) {
var (status, message) = result; var (status, message) = result; // Destructure the result
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)
.showSnackBar(SnackBar( .showSnackBar(SnackBar(
content: Text(message))); content: Text(message))); // Show snackbar with message
if (status) { if (status) { // Navigate to login page if successful
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const LoginPage()));
const LoginPage()));
} }
}); });
} }
}, },
), ),
), ),
), ),
@ -144,7 +159,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
), ),
), ),
); );
} },
); );
} }
} }

View File

@ -21,21 +21,22 @@ class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@override @override
State<HomePage> createState() => _HomePageState(); State<HomePage> createState() => _HomePageState(); // Create state for HomePage
} }
class _HomePageState extends State<HomePage> { class _HomePageState extends State<HomePage> {
NotificationServices notificationServices = NotificationServices(); NotificationServices notificationServices = NotificationServices(); // Initialize notification services
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// After the first frame is rendered, perform the following actions
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<HomeProvider>(context, listen: false).getProfile(); Provider.of<HomeProvider>(context, listen: false).getProfile(); // Fetch user profile data
notificationServices.requestNotificationPermission(); notificationServices.requestNotificationPermission(); // Request notification permissions
notificationServices.getDeviceToken().then((value) { notificationServices.getDeviceToken().then((value) {
if (kDebugMode) { if (kDebugMode) {
print('Device Token: $value'); print('Device Token: $value'); // Print device token in debug mode
} }
}); });
}); });
@ -55,31 +56,32 @@ class _HomePageState extends State<HomePage> {
color: Colors.black87, color: Colors.black87,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w400)), fontWeight: FontWeight.w400)),
Consumer<HomeProvider>( // Display user's name from profile data if available
builder: (context, value, child) => Consumer<HomeProvider>(builder: (context, value, child) =>
(value.profileResponse == null || (value.profileResponse == null ||
value.profileResponse!.myData == null || value.profileResponse!.myData == null ||
value.profileResponse!.myData!.name == null) value.profileResponse!.myData!.name == null)
? const SizedBox() ? const SizedBox()
: Text(value.profileResponse!.myData!.name ?? '', : Text(value.profileResponse!.myData!.name ?? '',
style: const TextStyle( style: const TextStyle(
color: Colors.black87, fontSize: 20))) color: Colors.black87, fontSize: 20)))
]) ])
]), ]),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0, // Remove elevation from the app bar
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Add custom drawer
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
double screenWidth = constraints.maxWidth; double screenWidth = constraints.maxWidth; // Get screen width
double buttonWidth = screenWidth / 2 - 18; // Adjust button width double buttonWidth = screenWidth / 2 - 18; // Calculate button width
return ListView( return ListView(
children: [ children: [
// Create custom card for marking attendance
_buildCustomCard( _buildCustomCard(
'Mark Attendance', 'Mark Attendance',
'Mark Attendance / On Leave', 'Mark Attendance / On Leave',
@ -94,6 +96,7 @@ class _HomePageState extends State<HomePage> {
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Create custom card for daily tasks
_buildCustomCard( _buildCustomCard(
'Daily Tasks', 'Daily Tasks',
'Dashboard', 'Dashboard',
@ -108,6 +111,7 @@ class _HomePageState extends State<HomePage> {
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Row containing cards for updating sales data and inventory
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -119,8 +123,7 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => SalesTaskScreen(),
SalesTaskScreen()
), ),
); );
}, },
@ -136,8 +139,7 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const UpdateInventoryTaskScreen(),
const UpdateInventoryTaskScreen(),
), ),
); );
}, },
@ -146,6 +148,7 @@ class _HomePageState extends State<HomePage> {
], ],
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Row containing cards for notifications and product purchase data
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -157,8 +160,7 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const NotificationScreen(),
const NotificationScreen(),
), ),
); );
}, },
@ -174,8 +176,7 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const ProductPurchaseData(),
const ProductPurchaseData(),
), ),
); );
}, },
@ -184,6 +185,7 @@ class _HomePageState extends State<HomePage> {
], ],
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Create custom card for collecting KYC data
_buildCustomCard( _buildCustomCard(
'Collect \nKYC Data', 'Collect \nKYC Data',
'Scan and upload KYC Documents', 'Scan and upload KYC Documents',
@ -198,6 +200,7 @@ class _HomePageState extends State<HomePage> {
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Create custom card for rejected applications
_buildCustomCard( _buildCustomCard(
'Rejected Applications', 'Rejected Applications',
'Re-upload Rejected Documents', 'Re-upload Rejected Documents',
@ -206,13 +209,13 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const RejectedApplicationScreen(),
const RejectedApplicationScreen(),
), ),
); );
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
// Row containing cards for calendar and products manual
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -240,8 +243,7 @@ class _HomePageState extends State<HomePage> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const ProductsManualScreen(),
const ProductsManualScreen(),
), ),
); );
}, },
@ -259,21 +261,22 @@ class _HomePageState extends State<HomePage> {
); );
} }
// Helper method to build a custom card with title, subtitle, and tap action
Widget _buildCustomCard(String title, String subtitle, double width, Widget _buildCustomCard(String title, String subtitle, double width,
{void Function()? onTap}) { {void Function()? onTap}) {
return Container( return Container(
width: width, width: width,
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10), // Add margin at the bottom
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.indigo, color: Colors.indigo, // Set card background color
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white), // Set border color
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10), // Set border radius
), ),
child: ListTile( child: ListTile(
title: Text( title: Text(
title, title,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white, // Set title text color
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
fontSize: 18, fontSize: 18,
fontFamily: 'Anek'), fontFamily: 'Anek'),
@ -281,10 +284,10 @@ class _HomePageState extends State<HomePage> {
subtitle: subtitle.isNotEmpty subtitle: subtitle.isNotEmpty
? Text( ? Text(
subtitle, subtitle,
style: const TextStyle(color: Colors.white70, fontSize: 13), style: const TextStyle(color: Colors.white70, fontSize: 13), // Set subtitle text color
) )
: null, : null,
onTap: onTap, onTap: onTap, // Assign tap action
), ),
); );
} }

View File

@ -13,168 +13,164 @@ class LoginPage extends StatefulWidget {
const LoginPage({super.key}); const LoginPage({super.key});
@override @override
State<LoginPage> createState() => _LoginPageState(); State<LoginPage> createState() => _LoginPageState(); // Create state for LoginPage
} }
class _LoginPageState extends State<LoginPage> { class _LoginPageState extends State<LoginPage> {
late LoginProvider loginProvider; late LoginProvider loginProvider; // Declare LoginProvider
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>(); // Global key for form validation
@override @override
void initState() { void initState() {
loginProvider = LoginProvider(); loginProvider = LoginProvider(); // Initialize LoginProvider
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider<LoginProvider>( return ChangeNotifierProvider<LoginProvider>( // Provide LoginProvider to the widget tree
create: (_) => loginProvider, create: (_) => loginProvider,
builder: (context, child) => Scaffold( builder: (context, child) => Scaffold(
body: CommonBackground( body: CommonBackground(
isFullWidth: false, isFullWidth: false,
child: Center( child: Center(
child: SingleChildScrollView( child: SingleChildScrollView( // Allow scrolling for the content
child: Column( child: Column(
children: [ children: [
const Text('Welcome', const Text('Welcome',
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Colors.white, color: Colors.white,
fontFamily: 'Roboto')), fontFamily: 'Roboto')),
const SizedBox(height: 50), const SizedBox(height: 50), // Spacer
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Container(decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12)), child: Container(
padding: const EdgeInsets.all(8), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), // Styling for logo container
child: Image.asset('assets/cheminova_logo.png', padding: const EdgeInsets.all(8),
height: kToolbarHeight - 25), child: Image.asset('assets/cheminova_logo.png', height: kToolbarHeight - 25), // Display logo
), ),
),
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: <Widget>[
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<LoginProvider>(
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<LoginProvider>(
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<LoginProvider>(
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()));
}
});
}
},
),
))
],
), ),
), 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: <Widget>[
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<LoginProvider>( // 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<LoginProvider>( // 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<LoginProvider>( // 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
}
});
}
},
),
)),
],
),
),
),
],
), ),
], ),
), ),
), )),
),
)),
); );
} }
} }

View File

@ -11,77 +11,79 @@ import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MarkAttendanceScreen extends StatefulWidget { class MarkAttendanceScreen extends StatefulWidget {
const MarkAttendanceScreen({super.key}); const MarkAttendanceScreen({super.key});
@override @override
State<MarkAttendanceScreen> createState() => _MarkAttendanceScreenState(); State<MarkAttendanceScreen> createState() => _MarkAttendanceScreenState(); // Create state for the screen
} }
class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> { class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
late AttendanceProvider attendanceProvider; late AttendanceProvider attendanceProvider; // Attendance provider instance
final dateController = TextEditingController( 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 = final timeController = TextEditingController(
TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); text: DateFormat('hh:mm a').format(DateTime.now())); // Initialize time controller with the current time
final locationController = TextEditingController(); final locationController = TextEditingController(); // Location controller for the user's current location
final notesController = TextEditingController(); final notesController = TextEditingController(); // Notes controller
@override @override
void initState() { void initState() {
super.initState(); super.initState();
attendanceProvider=AttendanceProvider(); attendanceProvider = AttendanceProvider(); // Initialize the attendance provider
_getCurrentLocation(); _getCurrentLocation(); // Fetch the user's current location
} }
Future<void> _getCurrentLocation() async { Future<void> _getCurrentLocation() async {
LocationPermission permission = await Geolocator.checkPermission(); LocationPermission permission = await Geolocator.checkPermission(); // Check location permission
if (permission == LocationPermission.denied) { if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission(); permission = await Geolocator.requestPermission(); // Request permission if denied
if (permission == LocationPermission.denied) { if (permission == LocationPermission.denied) {
debugPrint('location denied'); debugPrint('location denied'); // Log message if location permission is denied
return; return;
} }
} }
if (permission == LocationPermission.deniedForever) { if (permission == LocationPermission.deniedForever) {
setState(() { setState(() {
debugPrint('Location permissions are permanently denied'); debugPrint('Location permissions are permanently denied'); // Log if permissions are permanently denied
}); });
return; return;
} }
Position position = await Geolocator.getCurrentPosition( // Fetch user's current location with high accuracy
desiredAccuracy: LocationAccuracy.high); 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<Placemark> 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<Placemark> placeMarks = Placemark place = placeMarks[0]; // Use the first placemark
await placemarkFromCoordinates(position.latitude, position.longitude)
.timeout(const Duration(seconds: 10));
debugPrint('place mark--- $placeMarks');
Placemark place = placeMarks[0];
setState(() { setState(() {
locationController.text = place.locality??''; locationController.text = place.locality ?? ''; // Set locality to locationController
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider<AttendanceProvider>( return ChangeNotifierProvider<AttendanceProvider>( // Provide attendance provider to widget tree
create: (_) => attendanceProvider, create: (_) => attendanceProvider,
builder: (context, child) => CommonBackground( builder: (context, child) => CommonBackground(
child: Scaffold(backgroundColor: Colors.transparent, child: Scaffold(
backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { 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), padding: const EdgeInsets.only(right: 20),
), ),
], ],
@ -90,28 +92,29 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
fontSize: 28, fontSize: 28,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, 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( body: Stack(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(), // Smooth scrolling behavior
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(
padding: padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 20.0), margin: const EdgeInsets.symmetric(horizontal: 20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0)), borderRadius: BorderRadius.circular(26.0)), // Styling container
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
@ -119,26 +122,26 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
title: 'Date', title: 'Date',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: dateController), controller: dateController), // Date input field (read-only)
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Time', title: 'Time',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: timeController), controller: timeController), // Time input field (read-only)
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Location', title: 'Location',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: locationController), controller: locationController), // Location input field (read-only)
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
height: 100, height: 100,
title: 'Notes', title: 'Notes',
fillColor: Colors.white, fillColor: Colors.white,
maxLines: 4, maxLines: 4,
controller: notesController), controller: notesController), // Notes input field (editable)
const SizedBox(height: 16), const SizedBox(height: 16),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
@ -146,8 +149,8 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
children: [ children: [
Consumer<AttendanceProvider>( Consumer<AttendanceProvider>(
builder: (BuildContext context, builder: (BuildContext context,
AttendanceProvider value, AttendanceProvider value,
Widget? child) => Widget? child) =>
CommonElevatedButton( CommonElevatedButton(
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
borderRadius: 30, borderRadius: 30,
@ -157,27 +160,25 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
onPressed: () { onPressed: () {
value value
.markAttendance( .markAttendance(
dateController.text.trim(), dateController.text.trim(), // Pass date
timeController.text.trim(), timeController.text.trim(), // Pass time
locationController.text.trim(), locationController.text.trim(), // Pass location
notesController.text.trim()) notesController.text.trim()) // Pass notes
.then( .then(
(result) { (result) {
var (status, message) = result; var (status, message) = result; // Destructure the result
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)
.showSnackBar(SnackBar( .showSnackBar(SnackBar(
content: Text(message))); content: Text(message))); // Show a message based on the result
if (status) { if (status) {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const AttendanceSuccess())); const AttendanceSuccess())); // Navigate to success screen on success
} }
}, },
); );
// Navigator.push(context, MaterialPageRoute(builder:(context) => const AttendanceSuccess(),));
}), }),
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
@ -192,7 +193,7 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const OnLeaveScreen(), const OnLeaveScreen(), // Navigate to leave screen
)); ));
}) })
], ],
@ -211,16 +212,14 @@ class _MarkAttendanceScreenState extends State<MarkAttendanceScreen> {
right: 0, right: 0,
bottom: 0, bottom: 0,
child: Consumer<AttendanceProvider>(builder: (context, value, child) { child: Consumer<AttendanceProvider>(builder: (context, value, child) {
if(value.isLoading){ if (value.isLoading) { // Show loading indicator if loading
return return Container(
Container( color: Colors.black12,
color: Colors.black12, child: const Center(child: CircularProgressIndicator()));
child: const Center(child: CircularProgressIndicator())); } else {
return const SizedBox(); // Return empty widget if not loading
} }
else{ }),
return const SizedBox();
}
}, ),
) )
], ],
), ),

View File

@ -8,6 +8,7 @@ import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// Main screen that displays notifications
class NotificationScreen extends StatefulWidget { class NotificationScreen extends StatefulWidget {
const NotificationScreen({super.key}); const NotificationScreen({super.key});
@ -20,12 +21,14 @@ class NotificationScreenState extends State<NotificationScreen> {
@override @override
void initState() { void initState() {
// Initialize the NotificationProvider in the state when the screen is created
_notificationProvider = NotificationProvider(); _notificationProvider = NotificationProvider();
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Use ChangeNotifierProvider to supply NotificationProvider to the widget tree
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _notificationProvider, create: (context) => _notificationProvider,
child: CommonBackground( child: CommonBackground(
@ -35,6 +38,7 @@ class NotificationScreenState extends State<NotificationScreen> {
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
// Pop the screen when the back button is pressed
Navigator.pop(context); Navigator.pop(context);
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
@ -51,9 +55,12 @@ class NotificationScreenState extends State<NotificationScreen> {
elevation: 0, elevation: 0,
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
// Consumer listens for changes in the NotificationProvider
body: Consumer<NotificationProvider>( body: Consumer<NotificationProvider>(
builder: (context, value, child) => value.isLoading builder: (context, value, child) => value.isLoading
// Display a loading indicator if notifications are still loading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
// Show the list of notifications once loaded
: MyListView(value: value), : MyListView(value: value),
), ),
), ),
@ -62,6 +69,7 @@ class NotificationScreenState extends State<NotificationScreen> {
} }
} }
// Function that builds a button for a product
Widget buildProductButton(String productName) { Widget buildProductButton(String productName) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 15), padding: const EdgeInsets.only(bottom: 15),
@ -72,12 +80,14 @@ Widget buildProductButton(String productName) {
text: productName, text: productName,
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
// Log when the button is pressed
debugPrint('$productName pressed'); debugPrint('$productName pressed');
}, },
), ),
); );
} }
// Widget that displays a list of notifications grouped by date
class MyListView extends StatelessWidget { class MyListView extends StatelessWidget {
final NotificationProvider value; final NotificationProvider value;
@ -85,8 +95,10 @@ class MyListView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Group notifications by their creation date
Map<String, List<Notifications>> groupedNotifications = {}; Map<String, List<Notifications>> groupedNotifications = {};
// Iterate over the notification list and group by formatted date
for (var notification in value.notificationList) { for (var notification in value.notificationList) {
String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt ?? '')); String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt ?? ''));
if (!groupedNotifications.containsKey(date)) { if (!groupedNotifications.containsKey(date)) {
@ -95,9 +107,10 @@ class MyListView extends StatelessWidget {
groupedNotifications[date]!.add(notification); groupedNotifications[date]!.add(notification);
} }
// Build a ListView for grouped notifications
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.only(top: 15), padding: const EdgeInsets.only(top: 15),
itemCount: groupedNotifications.length, itemCount: groupedNotifications.length, // Number of date groups
itemBuilder: (context, index) { itemBuilder: (context, index) {
String date = groupedNotifications.keys.elementAt(index); String date = groupedNotifications.keys.elementAt(index);
List<Notifications> notificationsForDate = groupedNotifications[date]!; List<Notifications> notificationsForDate = groupedNotifications[date]!;
@ -107,7 +120,7 @@ class MyListView extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Display the date once // Display the date for each group of notifications
Padding( Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
child: Text( child: Text(
@ -115,12 +128,13 @@ class MyListView extends StatelessWidget {
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
), ),
// Display each notification in an expandable tile
...notificationsForDate.map((item) => Padding( ...notificationsForDate.map((item) => Padding(
padding: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.only(bottom: 10),
child: ExpansionTile( child: ExpansionTile(
collapsedBackgroundColor: Colors.white, collapsedBackgroundColor: Colors.white,
backgroundColor: Colors.white, backgroundColor: Colors.white,
trailing: const SizedBox.shrink(), trailing: const SizedBox.shrink(), // Remove trailing icon
title: Text( title: Text(
item.title ?? '', item.title ?? '',
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),

View File

@ -11,6 +11,7 @@ import '../widgets/common_app_bar.dart';
import '../widgets/common_elevated_button.dart'; import '../widgets/common_elevated_button.dart';
import '../widgets/common_text_form_field.dart'; import '../widgets/common_text_form_field.dart';
// Screen for marking leave
class OnLeaveScreen extends StatefulWidget { class OnLeaveScreen extends StatefulWidget {
const OnLeaveScreen({super.key}); const OnLeaveScreen({super.key});
@ -21,23 +22,26 @@ class OnLeaveScreen extends StatefulWidget {
class _OnLeaveScreenState extends State<OnLeaveScreen> { class _OnLeaveScreenState extends State<OnLeaveScreen> {
late MarkLeaveProvider _markLeaveProvider; late MarkLeaveProvider _markLeaveProvider;
// Controllers for input fields
final dateController = TextEditingController( final dateController = TextEditingController(
text: DateFormat('yyyy/MM/dd').format(DateTime.now())); text: DateFormat('yyyy/MM/dd').format(DateTime.now()));
final timeController = final timeController =
TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now()));
final locationController = TextEditingController(); final locationController = TextEditingController();
final notesController = 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) { void onLeaveTypeSelected(String leaveType) {
setState(() { setState(() {
selectedLeaveType = leaveType; selectedLeaveType = leaveType;
}); });
} }
// Widget to build an option for leave type selection
Widget buildLeaveTypeOption(String leaveType) { Widget buildLeaveTypeOption(String leaveType) {
bool isSelected = leaveType == selectedLeaveType; bool isSelected = leaveType == selectedLeaveType;
@ -67,17 +71,18 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_markLeaveProvider = MarkLeaveProvider(); _markLeaveProvider = MarkLeaveProvider(); // Initialize the leave provider
_getCurrentLocation(); _getCurrentLocation(); // Fetch the current location when the screen is initialized
} }
// Method to get the current location
Future<void> _getCurrentLocation() async { Future<void> _getCurrentLocation() async {
LocationPermission permission = await Geolocator.checkPermission(); LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) { if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission(); permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) { if (permission == LocationPermission.denied) {
debugPrint('location denied'); debugPrint('location denied');
return; return; // Exit if permission is denied
} }
} }
@ -85,21 +90,24 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
setState(() { setState(() {
debugPrint('Location permissions are permanently denied'); 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( Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high); desiredAccuracy: LocationAccuracy.high);
debugPrint('position--- $position'); debugPrint('position--- $position');
// Convert position coordinates to a human-readable address
List<Placemark> placeMarks = List<Placemark> placeMarks =
await placemarkFromCoordinates(position.latitude, position.longitude) await placemarkFromCoordinates(position.latitude, position.longitude)
.timeout(const Duration(seconds: 10)); .timeout(const Duration(seconds: 10));
debugPrint('place mark--- $placeMarks'); debugPrint('place mark--- $placeMarks');
Placemark place = placeMarks[0]; Placemark place = placeMarks[0];
setState(() { setState(() {
// Set the location in the text field
locationController.text = '${place.subLocality}, ${place.locality}'; locationController.text = '${place.subLocality}, ${place.locality}';
}); });
} }
@ -109,137 +117,142 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
return ChangeNotifierProvider<MarkLeaveProvider>( return ChangeNotifierProvider<MarkLeaveProvider>(
create: (_) => _markLeaveProvider, create: (_) => _markLeaveProvider,
builder: (context, child) => CommonBackground( builder: (context, child) => CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); // Navigate back when the back button is pressed
}, Navigator.pop(context);
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('On Leave',
style: TextStyle(
fontSize: 28,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
backgroundColor: Colors.transparent,
elevation: 0,
), ),
drawer: const CommonDrawer(), ],
body: Padding( title: const Text('On Leave',
padding: const EdgeInsets.all(16.0), style: TextStyle(
child: SingleChildScrollView( fontSize: 28,
physics: const BouncingScrollPhysics(), color: Colors.black,
child: Column( fontWeight: FontWeight.w400,
mainAxisAlignment: MainAxisAlignment.center, fontFamily: 'Anek')),
crossAxisAlignment: CrossAxisAlignment.center, backgroundColor: Colors.transparent,
children: <Widget>[ elevation: 0,
const SizedBox(height: 16), ),
Container( drawer: const CommonDrawer(),
padding: const EdgeInsets.all(20.0) body: Padding(
.copyWith(top: 30, bottom: 30), padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.symmetric(horizontal: 30.0), child: SingleChildScrollView(
decoration: BoxDecoration( physics: const BouncingScrollPhysics(),
border: Border.all(color: Colors.white), child: Column(
color: const Color(0xffB4D1E5).withOpacity(0.9), mainAxisAlignment: MainAxisAlignment.center,
borderRadius: BorderRadius.circular(26.0)), crossAxisAlignment: CrossAxisAlignment.center,
child: Column( children: <Widget>[
crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 16),
children: <Widget>[ Container(
CommonTextFormField( padding: const EdgeInsets.all(20.0)
title: 'Date', .copyWith(top: 30, bottom: 30),
readOnly: true, margin: const EdgeInsets.symmetric(horizontal: 30.0),
fillColor: Colors.white, decoration: BoxDecoration(
controller: dateController), border: Border.all(color: Colors.white),
const SizedBox(height: 15), color: const Color(0xffB4D1E5).withOpacity(0.9),
CommonTextFormField( borderRadius: BorderRadius.circular(26.0)),
title: 'Time', child: Column(
readOnly: true, crossAxisAlignment: CrossAxisAlignment.start,
fillColor: Colors.white, children: <Widget>[
controller: timeController), // Text field for date
const SizedBox(height: 15), CommonTextFormField(
const Text('Leave type', title: 'Date',
style: TextStyle( readOnly: true,
fontSize: 15, fillColor: Colors.white,
color: Colors.black, controller: dateController),
fontWeight: FontWeight.w400, const SizedBox(height: 15),
fontFamily: 'Anek')), // Text field for time
const SizedBox(height: 3), CommonTextFormField(
Wrap( title: 'Time',
direction: Axis.horizontal, readOnly: true,
children: [ fillColor: Colors.white,
buildLeaveTypeOption('Sick'), controller: timeController),
const SizedBox(width: 10), const SizedBox(height: 15),
buildLeaveTypeOption('Personal'), const Text('Leave type',
const SizedBox(width: 10), style: TextStyle(
buildLeaveTypeOption('Privilege '), fontSize: 15,
], color: Colors.black,
), fontWeight: FontWeight.w400,
const SizedBox(height: 15), fontFamily: 'Anek')),
CommonTextFormField( const SizedBox(height: 3),
height: 100, // Options for leave types
title: 'Reason', Wrap(
fillColor: Colors.white, direction: Axis.horizontal,
maxLines: 4, children: [
controller: notesController), buildLeaveTypeOption('Sick'),
const SizedBox(height: 16), const SizedBox(width: 10),
Align( buildLeaveTypeOption('Personal'),
alignment: Alignment.center, const SizedBox(width: 10),
child: Consumer<MarkLeaveProvider>( buildLeaveTypeOption('Privilege '),
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(),));
}),
)),
], ],
), ),
), 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<MarkLeaveProvider>(
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()));
}
},
);
}),
)),
],
),
), ),
), ],
), ),
), ),
)); ),
),
));
} }
} }

View File

@ -2,6 +2,7 @@ import 'package:cheminova/screens/home_screen.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// Screen to display after password change
class PasswordChangeScreen extends StatefulWidget { class PasswordChangeScreen extends StatefulWidget {
const PasswordChangeScreen({super.key}); const PasswordChangeScreen({super.key});
@ -13,6 +14,7 @@ class PasswordChangeScreenState extends State<PasswordChangeScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Navigate to the HomePage after a delay of 2 seconds
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement( Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage())); context, MaterialPageRoute(builder: (context) => const HomePage()));
@ -24,7 +26,7 @@ class PasswordChangeScreenState extends State<PasswordChangeScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: InkWell( leading: InkWell(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context), // Go back to the previous screen
child: Image.asset('assets/Back_attendance.png')), child: Image.asset('assets/Back_attendance.png')),
backgroundColor: Colors.transparent), backgroundColor: Colors.transparent),
body: CommonBackground( body: CommonBackground(

View File

@ -7,6 +7,7 @@ import '../widgets/common_app_bar.dart';
import '../widgets/common_elevated_button.dart'; import '../widgets/common_elevated_button.dart';
import '../widgets/common_text_form_field.dart'; import '../widgets/common_text_form_field.dart';
// Screen for entering product purchase data
class ProductPurchaseData extends StatefulWidget { class ProductPurchaseData extends StatefulWidget {
const ProductPurchaseData({super.key}); const ProductPurchaseData({super.key});
@ -16,11 +17,12 @@ class ProductPurchaseData extends StatefulWidget {
class ProductPurchaseDataState extends State<ProductPurchaseData> { class ProductPurchaseDataState extends State<ProductPurchaseData> {
final dateController = TextEditingController( 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 = 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 locationController = TextEditingController();
final notesController = TextEditingController(); final notesController = TextEditingController();
final dealercontroller = TextEditingController(); final dealercontroller = TextEditingController();
@ -29,19 +31,19 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
final salesController = TextEditingController(); final salesController = TextEditingController();
final commentController = TextEditingController(); final commentController = TextEditingController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( return CommonBackground(
child: Scaffold(backgroundColor: Colors.transparent, child: Scaffold(
backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () onPressed: () {
{ Navigator.pop(context); // Navigate back to the previous screen
Navigator.pop(context);
}, },
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', title: const Text('Product Purchase Data',
@ -49,7 +51,9 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek')), backgroundColor: Colors.transparent, elevation: 0, fontFamily: 'Anek')),
backgroundColor: Colors.transparent,
elevation: 0,
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
body: Padding( body: Padding(
@ -64,7 +68,6 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
Container( Container(
padding: padding:
const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
// margin: const EdgeInsets.symmetric(horizontal: 30.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9),
@ -72,18 +75,21 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// Input field for selecting a dealer
CommonTextFormField( CommonTextFormField(
title: 'Select Dealer: Dealer 1', title: 'Select Dealer: Dealer 1',
fillColor: Colors.white, fillColor: Colors.white,
controller: dealercontroller), controller: dealercontroller),
const SizedBox(height: 15), const SizedBox(height: 15),
// Input field for selecting a product
CommonTextFormField( CommonTextFormField(
title: 'Select Product: Product A', title: 'Select Product: Product A',
fillColor: Colors.white, fillColor: Colors.white,
controller: productController), controller: productController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Read-only input field for date range
CommonTextFormField( CommonTextFormField(
title: 'Date Range: 10-06 to 20-06', title: 'Date Range: 10-06 to 20-06',
readOnly: true, readOnly: true,
@ -91,26 +97,31 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
controller: dateController), controller: dateController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Button to view data, navigates to the success screen on press
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: CommonElevatedButton( child: CommonElevatedButton(
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'VIEW DATA', text: 'VIEW DATA',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen(),)), onPressed: () => Navigator.push(
context,
) MaterialPageRoute(
builder: (context) => const DataSubmitSuccessFullScreen(),
),
),
), ),
], ),
],
), ),
), ),
], ],
), ),
), ),
),), ),
),
); );
} }
} }

View File

@ -9,6 +9,7 @@ import 'package:provider/provider.dart';
import '../models/product_manual_model.dart'; import '../models/product_manual_model.dart';
import '../provider/product_manual_provider.dart'; import '../provider/product_manual_provider.dart';
// Screen to display product manuals
class ProductsManualScreen extends StatefulWidget { class ProductsManualScreen extends StatefulWidget {
const ProductsManualScreen({super.key}); const ProductsManualScreen({super.key});
@ -21,7 +22,7 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
@override @override
void initState() { void initState() {
_productManualProvider = ProductManualProvider(); _productManualProvider = ProductManualProvider(); // Initialize the product manual provider
super.initState(); super.initState();
} }
@ -36,7 +37,7 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Navigate back to the previous screen
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
@ -67,13 +68,13 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
builder: (context, provider, child) { builder: (context, provider, child) {
if (provider.isLoading) { if (provider.isLoading) {
return const Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(), // Show loading indicator while data is being fetched
); );
} }
if (provider.productManualList.isEmpty) { if (provider.productManualList.isEmpty) {
return const Center( return const Center(
child: Text('No products available.')); child: Text('No products available.')); // Message when no products are available
} }
return Container( return Container(
@ -89,8 +90,8 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
children: provider.productManualList children: provider.productManualList
.map( .map(
(product) => (product) =>
_buildProductButton(context, product), _buildProductButton(context, product), // Build buttons for each product
) )
.toList(), .toList(),
), ),
); );
@ -105,6 +106,7 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
); );
} }
// Method to build a button for each product
Widget _buildProductButton(BuildContext context, ProductManualModel product) { Widget _buildProductButton(BuildContext context, ProductManualModel product) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 15), padding: const EdgeInsets.only(bottom: 15),
@ -112,12 +114,12 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: product.title, text: product.title, // Display product title on the button
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () => Navigator.push( onPressed: () => Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => ViewPdfScreen(productManualModel: product), builder: (context) => ViewPdfScreen(productManualModel: product), // Navigate to the PDF view screen
), ),
), ),
), ),

View File

@ -4,6 +4,8 @@ import 'package:provider/provider.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
// Profile screen to display user information
class ProfileScreen extends StatelessWidget { class ProfileScreen extends StatelessWidget {
const ProfileScreen({Key? key}) : super(key: key); const ProfileScreen({Key? key}) : super(key: key);
@ -14,16 +16,16 @@ class ProfileScreen extends StatelessWidget {
CommonBackground( CommonBackground(
isFullWidth: true, isFullWidth: true,
child: Scaffold( child: Scaffold(
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Navigation drawer for the app
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
title: const Text('Profile'), title: const Text('Profile'), // Title of the app bar
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Navigate back to the previous screen
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
@ -32,33 +34,33 @@ class ProfileScreen extends StatelessWidget {
), ),
body: Consumer<HomeProvider>( body: Consumer<HomeProvider>(
builder: (context, profileProvider, child) { builder: (context, profileProvider, child) {
final profileData = profileProvider.profileResponse?.myData!; final profileData = profileProvider.profileResponse?.myData!; // Fetch user profile data
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
Container( 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, vertical: 20.0), margin: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0), 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 ?? ''),
],
),
), ),
], 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) { Widget _buildProfileItem(String label, String value) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 10), padding: const EdgeInsets.symmetric(vertical: 10),
@ -81,7 +84,7 @@ class ProfileScreen extends StatelessWidget {
color: Color(0xff004791), color: Color(0xff004791),
), ),
), ),
const SizedBox(height: 5), const SizedBox(height: 5), // Space between label and value
Text( Text(
value, value,
style: const TextStyle( style: const TextStyle(
@ -89,9 +92,9 @@ class ProfileScreen extends StatelessWidget {
color: Colors.black, color: Colors.black,
), ),
), ),
const Divider(color: Colors.grey), const Divider(color: Colors.grey), // Divider for visual separation
], ],
), ),
); );
} }
} }

View File

@ -9,6 +9,7 @@ import 'package:provider/provider.dart';
import '../provider/rejected_provider.dart'; import '../provider/rejected_provider.dart';
// Main screen for displaying rejected applications
class RejectedApplicationScreen extends StatefulWidget { class RejectedApplicationScreen extends StatefulWidget {
const RejectedApplicationScreen({super.key}); const RejectedApplicationScreen({super.key});
@ -22,14 +23,14 @@ class _RejectedApplicationScreenState extends State<RejectedApplicationScreen> {
@override @override
void initState() { void initState() {
_rejectedProvider = RejectedProvider(); _rejectedProvider = RejectedProvider(); // Initialize the provider for rejected applications
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _rejectedProvider, create: (context) => _rejectedProvider, // Provide the rejected provider to the widget tree
child: CommonBackground( child: CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -37,7 +38,7 @@ class _RejectedApplicationScreenState extends State<RejectedApplicationScreen> {
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Navigate back to the previous screen
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
@ -48,21 +49,22 @@ class _RejectedApplicationScreenState extends State<RejectedApplicationScreen> {
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek')), fontFamily: 'Anek')), // Title of the app bar
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Navigation drawer
body: Consumer<RejectedProvider>( body: Consumer<RejectedProvider>(
builder: (context, value, child) => value.isLoading builder: (context, value, child) => value.isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator()) // Loading indicator while data is being fetched
: MyListView(value: value), : MyListView(value: value), // Display the list of rejected applications
), ),
), ),
)); ));
} }
} }
// Button to display product information
Widget buildProductButton(String productName) { Widget buildProductButton(String productName) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 15), 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 { class MyListView extends StatelessWidget {
final RejectedProvider value; final RejectedProvider value;
@ -89,7 +92,7 @@ class MyListView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.only(top: 15), padding: const EdgeInsets.only(top: 15),
itemCount: value.rejectedApplicationList.length, itemCount: value.rejectedApplicationList.length, // Number of rejected applications
itemBuilder: (context, index) { itemBuilder: (context, index) {
RejectedApplicationResponse item = value.rejectedApplicationList[index]; RejectedApplicationResponse item = value.rejectedApplicationList[index];
return Padding( return Padding(
@ -98,75 +101,73 @@ class MyListView extends StatelessWidget {
collapsedBackgroundColor: Colors.white, collapsedBackgroundColor: Colors.white,
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: Text( title: Text(
item.tradeName ?? '', item.tradeName ?? '', // Trade name of the application
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
), ),
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text((DateFormat("dd/MMMM /yyyy") Text((DateFormat("dd/MMMM /yyyy")
.format(DateTime.parse(item.createdAt ?? '')))), .format(DateTime.parse(item.createdAt ?? '')))), // Formatted date of application
Text(item.sId ?? ''), Text(item.sId ?? ''), // Unique application ID
for (var note in item.notes!) Text(note.message ?? ''), for (var note in item.notes!) Text(note.message ?? ''), // Display application notes
], ],
), ),
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
title: Text('Address: ${item.address ?? ''}'), title: Text('Address: ${item.address ?? ''}'), // Application address
), ),
ListTile( ListTile(
title: Text('City: ${item.city ?? ''}'), title: Text('City: ${item.city ?? ''}'), // Application city
), ),
ListTile( ListTile(
title: Text('State: ${item.state ?? ''}'), title: Text('State: ${item.state ?? ''}'), // Application state
), ),
ListTile( ListTile(
title: Text('Pincode: ${item.pincode ?? ''}'), title: Text('Pincode: ${item.pincode ?? ''}'), // Application pincode
), ),
ListTile( ListTile(
title: Text('Mobile: ${item.mobileNumber ?? ''}'), title: Text('Mobile: ${item.mobileNumber ?? ''}'), // Mobile number associated with application
), ),
ListTile( ListTile(
title: Text('Status: ${item.status ?? ''}'), title: Text('Status: ${item.status ?? ''}'), // Current status of the application
), ),
ListTile( ListTile(
title: Text( title: Text(
'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), // Principal distributor's name
), ),
ListTile( 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( 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( 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( const ListTile(
title: Text('Pesticide License: '), title: Text('Pesticide License: '), // Pesticide license information
), ),
if (item.pesticideLicenseImg != null) if (item.pesticideLicenseImg != null)
Image.network(item.pesticideLicenseImg!.url ?? '', Image.network(item.pesticideLicenseImg!.url ?? '',
height: 250, width: 250), height: 250, width: 250), // Pesticide license image
// if (item['fertilizer_license_img'] != null)
// Image.network(item['fertilizer_license_img']['url'] ?? ''),
const ListTile( const ListTile(
title: Text('selfieEntranceImg: '), title: Text('selfieEntranceImg: '), // Selfie entrance image information
), ),
Image.network(item.selfieEntranceImg!.url ?? '', Image.network(item.selfieEntranceImg!.url ?? '',
height: 250, width: 250), height: 250, width: 250), // Selfie entrance image
const ListTile( const ListTile(
title: Text('Notes:'), title: Text('Notes:'), // Display notes for the application
), ),
if (item.notes != null) if (item.notes != null)
for (var note in item.notes!) for (var note in item.notes!) // Iterate over notes and display each
ListTile( ListTile(
contentPadding: const EdgeInsets.only(left: 20), contentPadding: const EdgeInsets.only(left: 20),
title: Text(note.message ?? ''), title: Text(note.message ?? ''), // Note message
), ),
], ],
), ),

View File

@ -31,175 +31,186 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
const SizedBox(height: 16), const SizedBox(height: 16),
// Container for the retailer details form
Container( Container(
padding: const EdgeInsets.all(20.0) padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
.copyWith(top: 30, bottom: 30),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0)), borderRadius: BorderRadius.circular(26.0)),
child: Form( child: Form(
key: value.retailerDetailsFormKey, key: value.retailerDetailsFormKey, // Key to manage form state
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Column( Column(
children: [ children: [
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Trade Name
CommonTextFormField( CommonTextFormField(
title: 'Trade Name', title: 'Trade Name',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Trade Name cannot be empty'; return 'Trade Name cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.tradeNameController), controller: value.tradeNameController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Name
CommonTextFormField( CommonTextFormField(
title: 'Name', title: 'Name',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Name cannot be empty'; return 'Name cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.nameController), controller: value.nameController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Address
CommonTextFormField( CommonTextFormField(
title: 'Address', title: 'Address',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Address cannot be empty'; return 'Address cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.addressController), controller: value.addressController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Country, State, and City Picker
CSCPicker( CSCPicker(
defaultCountry: CscCountry.India, defaultCountry: CscCountry.India,
disableCountry: true, disableCountry: true,
onCountryChanged: (val) { onCountryChanged: (val) {
setState(() { setState(() {
value.country.text = val; value.country.text = val; // Update country
}); });
}, },
onStateChanged: (val) { onStateChanged: (val) {
setState(() { setState(() {
value.state.text = val ?? ''; value.state.text = val ?? ''; // Update state
}); });
}, },
onCityChanged: (val) { onCityChanged: (val) {
setState(() { setState(() {
value.city.text = val ?? ''; value.city.text = val ?? ''; // Update city
}); });
}, },
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for District
CommonTextFormField( CommonTextFormField(
title: 'District', title: 'District',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'District cannot be empty'; return 'District cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.districtController), controller: value.districtController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Pincode
CommonTextFormField( CommonTextFormField(
maxLength: 6, maxLength: 6,
title: 'Pincode', title: 'Pincode',
fillColor: Colors.white, fillColor: Colors.white,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly // Only digits allowed
], ],
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Pincode cannot be empty'; return 'Pincode cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.pinCodeController), controller: value.pinCodeController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Mobile Number
CommonTextFormField( CommonTextFormField(
maxLength: 10, maxLength: 10,
title: 'Mobile Number', title: 'Mobile Number',
fillColor: Colors.white, fillColor: Colors.white,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly // Only digits allowed
], ],
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Mobile Number cannot be empty'; return 'Mobile Number cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.mobileNumberController), controller: value.mobileNumberController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Email
CommonTextFormField( CommonTextFormField(
// maxLength: 20,
title: 'Email', title: 'Email',
fillColor: Colors.white, fillColor: Colors.white,
inputFormatters: [ inputFormatters: [
NoSpaceFormatter(), NoSpaceFormatter(), // No spaces allowed
], ],
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Email cannot be empty'; return 'Email cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.emailController), controller: value.emailController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for Aadhar Number
CommonTextFormField( CommonTextFormField(
maxLength: 12, maxLength: 12,
title: 'Aadhar Number', title: 'Aadhar Number',
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly // Only digits allowed
], ],
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'Aadhar Number cannot be empty'; return 'Aadhar Number cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.aadharNumberController), controller: value.aadharNumberController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for PAN Number
CommonTextFormField( CommonTextFormField(
inputFormatters: [ inputFormatters: [
UpperCaseTextFormatter(), UpperCaseTextFormatter(), // Convert to uppercase
], ],
maxLength: 10, maxLength: 10,
title: 'PAN Number', title: 'PAN Number',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'PAN Number cannot be empty'; return 'PAN Number cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.panNumberController), controller: value.panNumberController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Text field for GST Number
CommonTextFormField( CommonTextFormField(
inputFormatters: [ inputFormatters: [
UpperCaseTextFormatter(), UpperCaseTextFormatter(), // Convert to uppercase
], ],
maxLength: 15, maxLength: 15,
title: 'GST Number', title: 'GST Number',
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return 'GST Number cannot be empty'; return 'GST Number cannot be empty'; // Validation message
} }
return null; return null;
}, },
controller: value.gstNumberController), controller: value.gstNumberController),
const SizedBox(height: 15), const SizedBox(height: 15),
// Dropdown for selecting Mapped Principal Distributor
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
decoration: InputDecoration( decoration: InputDecoration(
fillColor: Colors.white, fillColor: Colors.white,
@ -215,17 +226,18 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
}).toList(), }).toList(),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
value.selectedDistributor = newValue; value.selectedDistributor = newValue; // Update selected distributor
}); });
}, },
validator: (String? value) { validator: (String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Mapped Principal Distributor cannot be empty'; return 'Mapped Principal Distributor cannot be empty'; // Validation message
} }
return null; return null;
}, },
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
// Continue button to navigate to the next step
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: CommonElevatedButton( child: CommonElevatedButton(
@ -236,8 +248,8 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
if (value.retailerDetailsFormKey.currentState! if (value.retailerDetailsFormKey.currentState!
.validate()) { .validate()) { // Validate form
value.tabController.animateTo(1); value.tabController.animateTo(1); // Navigate to next tab
} }
}, },
), ),

View File

@ -22,10 +22,11 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_dailyTaskProvider = DailyTaskProvider(); _dailyTaskProvider = DailyTaskProvider(); // Initialize the provider
apiCall(); apiCall(); // Fetch tasks when the screen initializes
} }
// Function to call API for fetching tasks
void apiCall() async { void apiCall() async {
await _dailyTaskProvider.getTask(type: 'New', isAddPending: true); await _dailyTaskProvider.getTask(type: 'New', isAddPending: true);
await _dailyTaskProvider.getTask(type: 'Pending', isAddPending: true); await _dailyTaskProvider.getTask(type: 'Pending', isAddPending: true);
@ -34,27 +35,28 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _dailyTaskProvider, create: (context) => _dailyTaskProvider, // Provide the daily task provider
child: Scaffold( child: Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: _buildAppBar(), appBar: _buildAppBar(), // Build the app bar
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Add a common drawer
body: CommonBackground( body: CommonBackground(
child: SafeArea( child: SafeArea(
child: _buildTaskList(), child: _buildTaskList(), // Build the task list
), ),
), ),
), ),
); );
} }
// Method to build the app bar
CommonAppBar _buildAppBar() { CommonAppBar _buildAppBar() {
return CommonAppBar( return CommonAppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
actions: [ actions: [
IconButton( IconButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context), // Back button functionality
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
@ -66,18 +68,21 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
); );
} }
// Method to build the task list
Widget _buildTaskList() { Widget _buildTaskList() {
return Consumer<DailyTaskProvider>( return Consumer<DailyTaskProvider>(
builder: (context, value, child) { builder: (context, value, child) {
if (value.isLoading) { 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 final salesTasks = value.newTasksList
.where((task) => .where((task) =>
task.task?.toLowerCase() == 'Update Sales Data'.toLowerCase()) task.task?.toLowerCase() == 'Update Sales Data'.toLowerCase())
.toList(); .toList();
// If no tasks are found, display a message
if (salesTasks.isEmpty) { if (salesTasks.isEmpty) {
return const Center( return const Center(
child: Text( child: Text(
@ -91,19 +96,22 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
); );
} }
// Build the list of tasks
return ListView.separated( return ListView.separated(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: salesTasks.length, itemCount: salesTasks.length,
separatorBuilder: (context, index) => const SizedBox(height: 8), 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) { Widget _buildTaskCard(Tasks tasksList) {
return InkWell( return InkWell(
onTap: () { onTap: () {
// Navigate to AddSalesProductScreen with the selected task details
if (tasksList.sId != null && tasksList.addedFor != null) { if (tasksList.sId != null && tasksList.addedFor != null) {
Navigator.push( Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
@ -119,9 +127,9 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
color: Colors.white, color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: ListTile( 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( title: Text(
tasksList.task ?? '', tasksList.task ?? '', // Task name
style: const TextStyle( style: const TextStyle(
color: Colors.black87, color: Colors.black87,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@ -132,15 +140,15 @@ class _SalesTaskScreenState extends State<SalesTaskScreen> {
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Distributor: ${tasksList.addedFor ?? ""}'), Text('Distributor: ${tasksList.addedFor ?? ""}'), // Distributor information
Text('Trader Name: ${tasksList.tradeName ?? ''}'), Text('Trader Name: ${tasksList.tradeName ?? ''}'), // Trader name
if (tasksList.taskDueDate != null) if (tasksList.taskDueDate != null)
Text( Text(
'Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), 'Due Date: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(tasksList.taskDueDate!))}'), // Formatted due date
Text('Priority: ${tasksList.taskPriority}'), 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
), ),
), ),
); );

View File

@ -20,48 +20,49 @@ class SelectTaskkycScreenState extends State<SelectTaskkycScreen> {
@override @override
void initState() { void initState() {
_selectTaskProvider = SelectTaskProvider(); _selectTaskProvider = SelectTaskProvider(); // Initialize the SelectTaskProvider
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _selectTaskProvider, create: (context) => _selectTaskProvider, // Provide the SelectTaskProvider
child: CommonBackground( child: CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
title: const Text('Select Task'), title: const Text('Select Task'), // Title for the app bar
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
actions: [ actions: [
IconButton( IconButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context), // Back button functionality
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Common navigation drawer
body: Consumer<SelectTaskProvider>( body: Consumer<SelectTaskProvider>(
builder: (context, value, child) => builder: (context, value, child) =>
value.isLoading value.isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator()) // Loading indicator while tasks are fetched
: _buildTaskList(), : _buildTaskList(), // Build the task list once loading is complete
), ),
), ),
), ),
); );
} }
// Method to build the list of tasks
Widget _buildTaskList() { Widget _buildTaskList() {
return Consumer<SelectTaskProvider>( return Consumer<SelectTaskProvider>(
builder: (context, value, child) { builder: (context, value, child) {
if (value.tasksList.isEmpty) { if (value.tasksList.isEmpty) {
return const Center( return const Center(
child: Text( child: Text(
'NO TASK', 'NO TASK', // Message displayed if no tasks are available
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -71,32 +72,33 @@ class SelectTaskkycScreenState extends State<SelectTaskkycScreen> {
); );
} }
return ListView.builder( return ListView.builder(
itemCount: value.tasksList.length, itemCount: value.tasksList.length, // Number of tasks to display
itemBuilder: (context, index) => _buildTaskCard(value.tasksList[index]), itemBuilder: (context, index) => _buildTaskCard(value.tasksList[index]), // Build each task card
); );
}, },
); );
} }
// Method to build an individual task card
Widget _buildTaskCard(Tasks task) { Widget _buildTaskCard(Tasks task) {
return InkWell( return InkWell(
onTap: () => Navigator.push( onTap: () => Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CollectKycScreen(id: task.sId ?? ''), builder: (context) => CollectKycScreen(id: task.sId ?? ''), // Navigate to CollectKycScreen
), ),
), ),
child: Card( 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, color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ListTile( ListTile(
leading: const Icon(Icons.task, color: Colors.blueAccent), leading: const Icon(Icons.task, color: Colors.blueAccent), // Task icon
title: Text( title: Text(
task.task ?? '', task.task ?? '', // Task title
style: const TextStyle( style: const TextStyle(
color: Colors.black87, color: Colors.black87,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@ -104,19 +106,19 @@ class SelectTaskkycScreenState extends State<SelectTaskkycScreen> {
fontFamily: 'Anek', 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(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Note: ${task.note}'), Text('Note: ${task.note}'), // Display task note
if (task.taskDueDate != null) if (task.taskDueDate != null)
Text( 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<SelectTaskkycScreen> {
} }
} }
// Base class for task items
class TaskItem { class TaskItem {
final String title; final String title; // Title of the task
final Widget screen; final Widget screen; // Screen to navigate to
TaskItem({required this.title, required this.screen}); TaskItem({required this.title, required this.screen});
} }
// Extended class for KYC task items with additional attributes
class KycTaskItem extends TaskItem { class KycTaskItem extends TaskItem {
final String note; final String note; // Note associated with the task
final String date; final String date; // Due date of the task
final String priority; final String priority; // Priority of the task
KycTaskItem({ KycTaskItem({
required String title, required String title,
@ -146,4 +150,4 @@ class KycTaskItem extends TaskItem {
required this.date, required this.date,
required this.priority, required this.priority,
}) : super(title: title, screen: screen); }) : super(title: title, screen: screen);
} }

View File

@ -16,59 +16,75 @@ class _SplashScreenState extends State<SplashScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Delay for 3 seconds to show the splash screen before checking user authentication
Future.delayed(const Duration(seconds: 3), () { Future.delayed(const Duration(seconds: 3), () {
SecureStorageService().read( // Read the access token from secure storage
key: 'access_token').then((value){ SecureStorageService().read(key: 'access_token').then((value) {
if(value != null){ // Check if the access token exists
Navigator.pushReplacement( if (value != null) {
context, MaterialPageRoute(builder: (context) => const HomePage())); // If the token exists, navigate to the HomePage
}else{ Navigator.pushReplacement(
Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage()));
context, MaterialPageRoute(builder: (context) => const LoginPage())); } else {
} // If the token doesn't exist, navigate to the LoginPage
}); Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const LoginPage()));
}
});
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white, // Background color of the splash screen
body: Container( body: Container(
width: MediaQuery.sizeOf(context).width, width: MediaQuery.sizeOf(context).width, // Set width to full screen width
height: MediaQuery.sizeOf(context).height, height: MediaQuery.sizeOf(context).height, // Set height to full screen height
padding: const EdgeInsets.only(top: 110, bottom: 50), padding: const EdgeInsets.only(top: 110, bottom: 50), // Padding for the content
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween, // Space between children
children: <Widget>[ children: <Widget>[
Column( Column(
children: [ children: [
Image.asset('assets/cheminova_logo.png'), Image.asset('assets/cheminova_logo.png'), // App logo
const Text('HELPING YOU GROW', const Text(
style: TextStyle( 'HELPING YOU GROW', // Tagline text
color: Colors.black, style: TextStyle(
fontFamily: robotoFamily, color: Colors.black,
fontSize: 20, fontFamily: robotoFamily,
fontWeight: FontWeight.w500)), fontSize: 20,
], fontWeight: FontWeight.w500,
), ),
Image.asset('assets/plant.png'), ),
const Column( ],
children: [ ),
Text('Powered By', Image.asset('assets/plant.png'), // Image displayed at the center
style: TextStyle( const Column(
color: Colors.black, children: [
fontSize: 12, Text(
fontFamily: robotoFamily, 'Powered By', // Label indicating the provider
fontWeight: FontWeight.w500)), style: TextStyle(
Text('codeology.solutions', color: Colors.black,
style: TextStyle( fontSize: 12,
color: Colors.black, fontFamily: robotoFamily,
fontFamily: robotoFamily, fontWeight: FontWeight.w500,
fontSize: 14,
fontWeight: FontWeight.w700)),
],
), ),
]))); ),
Text(
'codeology.solutions', // Company name
style: TextStyle(
color: Colors.black,
fontFamily: robotoFamily,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
),
);
} }
} }

View File

@ -15,104 +15,108 @@ class SummaryScreen extends StatefulWidget {
} }
class SummaryScreenState extends State<SummaryScreen> { class SummaryScreenState extends State<SummaryScreen> {
// Controllers for managing text input fields
final dateController = TextEditingController( 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 = 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 ProductController = TextEditingController(); // Controller for product input
final liquidationController = TextEditingController(); final liquidationController = TextEditingController(); // Controller for liquidation input
final dealercontroller = TextEditingController(); final dealercontroller = TextEditingController(); // Controller for dealer input
final inventoryController = TextEditingController(); final inventoryController = TextEditingController(); // Controller for inventory input
final qualityController = TextEditingController(); final qualityController = TextEditingController(); // Controller for quality input
final salesController = TextEditingController(); final salesController = TextEditingController(); // Controller for sales input
final skuController = TextEditingController(); final skuController = TextEditingController(); // Controller for SKU input
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( return CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Transparent background
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
// Back button in the app bar
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Navigate back to the previous screen
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
title: const Text('Summary', title: const Text(
style: TextStyle( 'Summary',
fontSize: 20, style: TextStyle(
color: Colors.black, fontSize: 20,
fontWeight: FontWeight.w400, color: Colors.black,
fontFamily: 'Anek')), fontWeight: FontWeight.w400,
backgroundColor: Colors.transparent, fontFamily: 'Anek'),
elevation: 0, ),
backgroundColor: Colors.transparent, // Transparent app bar background
elevation: 0, // No shadow
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Drawer for navigation
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0), // Padding around the body content
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(), // Enable bouncing scroll physics
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, // Center the content vertically
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, // Center the content horizontally
children: <Widget>[ children: <Widget>[
const SizedBox(height: 16), const SizedBox(height: 16), // Spacing
Container( Container(
padding: padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), // Container styling
// margin: const EdgeInsets.symmetric(horizontal: 30.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white), // White border
color: const Color(0xffB4D1E5).withOpacity(0.9), color: const Color(0xffB4D1E5).withOpacity(0.9), // Light blue background
borderRadius: BorderRadius.circular(26.0)), borderRadius: BorderRadius.circular(26.0)), // Rounded corners
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align children to the start
children: <Widget>[ children: <Widget>[
// Input fields for summary data
CommonTextFormField( CommonTextFormField(
title: 'Sales :', title: 'Sales :',
fillColor: Colors.white, fillColor: Colors.white,
controller: salesController), controller: salesController), // Sales input field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Inventory :', title: 'Inventory :',
fillColor: Colors.white, fillColor: Colors.white,
controller: inventoryController), controller: inventoryController), // Inventory input field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Liquidation :', title: 'Liquidation :',
fillColor: Colors.white, fillColor: Colors.white,
controller: liquidationController), controller: liquidationController), // Liquidation input field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'SKU :', title: 'SKU :',
fillColor: Colors.white, fillColor: Colors.white,
controller: skuController), controller: skuController), // SKU input field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Product :', title: 'Product :',
fillColor: Colors.white, fillColor: Colors.white,
controller: ProductController), controller: ProductController), // Product input field
const SizedBox(height: 15), const SizedBox(height: 15),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: CommonElevatedButton( child: CommonElevatedButton(
borderRadius: 30, borderRadius: 30, // Rounded corners for the button
width: double.infinity, width: double.infinity, // Full width button
height: kToolbarHeight - 10, height: kToolbarHeight - 10, // Button height
text: 'VIEW DATA', text: 'VIEW DATA', // Button text
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791), // Button background color
onPressed: () { onPressed: () {
// Navigate to DataSubmitSuccessFullScreen on button press
Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen())); Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessFullScreen()));
},
})
), ),
),
], ],
), ),
), ),

View File

@ -14,97 +14,111 @@ class UpdateInventoryScreen extends StatefulWidget {
} }
class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> { class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
late PdRdProvider pdRdProvider; late PdRdProvider pdRdProvider; // Provider for managing inventory data
@override @override
void initState() { void initState() {
super.initState(); super.initState();
pdRdProvider = PdRdProvider(); pdRdProvider = PdRdProvider(); // Initialize the provider
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => pdRdProvider, create: (context) => pdRdProvider, // Provide the PdRdProvider to the widget tree
child: CommonBackground( child: CommonBackground(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent, // Set the background to transparent
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( // Back button in the app bar
onPressed: () => Navigator.pop(context), IconButton(
icon: Image.asset('assets/Back_attendance.png'), onPressed: () => Navigator.pop(context), // Navigate back
padding: const EdgeInsets.only(right: 20)) icon: Image.asset('assets/Back_attendance.png'),
], padding: const EdgeInsets.only(right: 20),
title: const Text('Update Inventory Data', ),
style: TextStyle( ],
fontSize: 20, title: const Text('Update Inventory Data',
color: Colors.black, style: TextStyle(
fontWeight: FontWeight.w400, fontSize: 20,
fontFamily: 'Anek')), color: Colors.black,
backgroundColor: Colors.transparent, fontWeight: FontWeight.w400,
elevation: 0), fontFamily: 'Anek')), // Title of the app bar
drawer: const CommonDrawer(), backgroundColor: Colors.transparent, // Transparent app bar background
body: Stack(children: [ elevation: 0, // No shadow for the app bar
Consumer<PdRdProvider>( ),
builder: (context, value, child) => Column(children: [ drawer: const CommonDrawer(), // Drawer for navigation
Padding( body: Stack(
padding: const EdgeInsets.symmetric( children: [
horizontal: 15.0, vertical: 25), Consumer<PdRdProvider>(
child: DropdownButtonFormField<String>( builder: (context, value, child) => Column(
decoration: const InputDecoration( children: [
fillColor: Colors.white, Padding(
filled: true, padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25),
border: OutlineInputBorder()), child: DropdownButtonFormField<String>(
value: value.selectedDistributorType, decoration: const InputDecoration(
items: [ fillColor: Colors.white,
'Principal Distributor', filled: true,
'Retail Distributor' border: OutlineInputBorder()),
].map((String type) { value: value.selectedDistributorType, // Currently selected distributor type
return DropdownMenuItem<String>( items: [
value: type, child: Text(type)); 'Principal Distributor',
}).toList(), 'Retail Distributor'
hint: const Text('Select Distributor Type'), ].map((String type) {
onChanged: (val) => return DropdownMenuItem<String>(
value.updateDistributorType(val))), value: type, child: Text(type)); // Create dropdown items
Padding( }).toList(),
padding: const EdgeInsets.symmetric( hint: const Text('Select Distributor Type'), // Hint text
horizontal: 15.0, vertical: 25), onChanged: (val) =>
child: DropdownButtonFormField<GetPdRdResponse>( value.updateDistributorType(val), // Update selected distributor type
decoration: const InputDecoration( ),
fillColor: Colors.white, ),
filled: true, Padding(
border: OutlineInputBorder()), padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25),
value: value.selectedPdRd, child: DropdownButtonFormField<GetPdRdResponse>(
items: value.pdRdList.map((e) { decoration: const InputDecoration(
return DropdownMenuItem<GetPdRdResponse>( fillColor: Colors.white,
value: e, child: Text(e.name ?? '')); filled: true,
}).toList(), border: OutlineInputBorder()),
onChanged: (val) => value: value.selectedPdRd, // Currently selected distributor
value.updatePdRdValue(val), items: value.pdRdList.map((e) {
isExpanded: true, return DropdownMenuItem<GetPdRdResponse>(
isDense: true, value: e, child: Text(e.name ?? '')); // Create dropdown items
iconSize: 24, }).toList(),
hint: onChanged: (val) =>
const Text('Select Distributor Name'))) value.updatePdRdValue(val), // Update selected distributor value
])), isExpanded: true,
Consumer<PdRdProvider>( isDense: true,
builder: (context, value, child) => value.isLoading iconSize: 24,
? Container( hint: const Text('Select Distributor Name'), // Hint text
color: Colors.black12, ),
child: const Center( )
child: CircularProgressIndicator())) ],
: const SizedBox()) ),
])))); ),
Consumer<PdRdProvider>(
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 { class Product {
final String name; final String name; // Product name
final String sku; final String sku; // Stock keeping unit
final bool isPurchased; final bool isPurchased; // Purchase status
int? sale; int? sale; // Sales count
int? inventory; int? inventory; // Inventory count
Product({ Product({
required this.name, required this.name,
@ -115,8 +129,9 @@ class Product {
}); });
} }
// Widget to represent a block of product details
class ProductBlock extends StatefulWidget { class ProductBlock extends StatefulWidget {
final Product product; final Product product; // Product data passed to the widget
const ProductBlock({super.key, required this.product}); const ProductBlock({super.key, required this.product});
@ -125,30 +140,30 @@ class ProductBlock extends StatefulWidget {
} }
class _ProductBlockState extends State<ProductBlock> { class _ProductBlockState extends State<ProductBlock> {
final saleController = TextEditingController(); final saleController = TextEditingController(); // Controller for sale input
final inventoryController = TextEditingController(); final inventoryController = TextEditingController(); // Controller for inventory input
String? errorMessage; String? errorMessage; // Variable to hold error messages
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Initialize controllers with product data
saleController.text = widget.product.sale?.toString() ?? ''; saleController.text = widget.product.sale?.toString() ?? '';
inventoryController.text = widget.product.inventory?.toString() ?? ''; inventoryController.text = widget.product.inventory?.toString() ?? '';
} }
void validateInput() { void validateInput() {
setState(() { setState(() {
if (saleController.text.isNotEmpty && if (saleController.text.isNotEmpty && inventoryController.text.isNotEmpty) {
inventoryController.text.isNotEmpty) { int sale = int.parse(saleController.text); // Parse sale input
int sale = int.parse(saleController.text); int inventory = int.parse(inventoryController.text); // Parse inventory input
int inventory = int.parse(inventoryController.text);
if (inventory > sale) { 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 { } else {
errorMessage = null; errorMessage = null; // Clear error message if valid
} }
} else { } else {
errorMessage = null; errorMessage = null; // Clear error message if fields are empty
} }
}); });
} }
@ -156,38 +171,36 @@ class _ProductBlockState extends State<ProductBlock> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
color: !widget.product.isPurchased ? Colors.white54 : Colors.white, color: !widget.product.isPurchased ? Colors.white54 : Colors.white, // Change color based on purchase status
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, // Align children to the start
children: [ children: [
Text('Product: ${widget.product.name}', Text('Product: ${widget.product.name}', style: const TextStyle(fontSize: 16)), // Display product name
style: const TextStyle(fontSize: 16)), Text('SKU: ${widget.product.sku}', style: const TextStyle(fontSize: 15)), // Display SKU
Text('SKU: ${widget.product.sku}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8), const SizedBox(height: 8),
TextField( TextField(
controller: saleController, controller: saleController, // Controller for sale input field
decoration: const InputDecoration(labelText: 'Sale'), decoration: const InputDecoration(labelText: 'Sale'), // Label for sale input
keyboardType: TextInputType.number, keyboardType: TextInputType.number, // Numeric keyboard
enabled: widget.product.isPurchased, enabled: widget.product.isPurchased, // Enable input based on purchase status
onChanged: (_) => validateInput(), onChanged: (_) => validateInput(), // Validate input on change
), ),
TextField( TextField(
controller: inventoryController, controller: inventoryController, // Controller for inventory input field
decoration: const InputDecoration(labelText: 'Inventory'), decoration: const InputDecoration(labelText: 'Inventory'), // Label for inventory input
keyboardType: TextInputType.number, keyboardType: TextInputType.number, // Numeric keyboard
enabled: widget.product.isPurchased, enabled: widget.product.isPurchased, // Enable input based on purchase status
onChanged: (_) => validateInput(), onChanged: (_) => validateInput(), // Validate input on change
), ),
if (errorMessage != null) if (errorMessage != null) // Display error message if exists
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Text( child: Text(
errorMessage!, errorMessage!,
style: const TextStyle(color: Colors.red), style: const TextStyle(color: Colors.red), // Style for error message
), ),
), ),
], ],

View File

@ -17,35 +17,38 @@ class UploadDocumentScreen extends StatefulWidget {
} }
class UploadDocumentScreenState extends State<UploadDocumentScreen> { class UploadDocumentScreenState extends State<UploadDocumentScreen> {
// Function to build the upload button and file view
Widget _buildUploadButton(String title, File? file, bool isOptional) { Widget _buildUploadButton(String title, File? file, bool isOptional) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Display title with optional label if applicable
Text( Text(
title + (isOptional ? ' (optional)' : ''), title + (isOptional ? ' (optional)' : ''),
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
/// Create a view for the selected file // Create a view for the selected file
if (file != null) if (file != null)
Container( Container(
height: 100, height: 100,
width: 100, width: 100,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.grey), border: Border.all(color: Colors.grey), // Border for the file container
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8), // Rounded corners
), ),
child: file.path.endsWith('.jpg') || file.path.endsWith('.png') 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( : Center(
child: Text(file.path.split('/').last, child: Text(file.path.split('/').last, // Show file name if not an image
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontWeight: FontWeight.bold), fontSize: 12, fontWeight: FontWeight.bold),
textAlign: TextAlign.center))) textAlign: TextAlign.center)))
else 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) if (file != null)
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
@ -57,8 +60,10 @@ class UploadDocumentScreenState extends State<UploadDocumentScreen> {
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
// Change button text based on file presence
text: file == null ? 'Upload $title' : 'Change $title', text: file == null ? 'Upload $title' : 'Change $title',
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
// Trigger the image picker on button press
onPressed: () => value.showPicker(context, title), onPressed: () => value.showPicker(context, title),
), ),
), ),
@ -73,7 +78,7 @@ class UploadDocumentScreenState extends State<UploadDocumentScreen> {
builder: (context, value, child) => Padding( builder: (context, value, child) => Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(), // Enable bouncing scroll
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -81,26 +86,22 @@ class UploadDocumentScreenState extends State<UploadDocumentScreen> {
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(
padding: padding:
const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), 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)), borderRadius: BorderRadius.circular(26.0)),
child: Consumer<CollectKycProvider>( child: Consumer<CollectKycProvider>(
builder: (context, value, child) => Column( builder: (context, value, child) => Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// Build upload buttons for each document type
_buildUploadButton('PAN Card', value.panCard, false), _buildUploadButton('PAN Card', value.panCard, false),
_buildUploadButton( _buildUploadButton('Aadhar Card', value.aadharCard, false),
'Aadhar Card', value.aadharCard, false), _buildUploadButton('GST Registration', value.gstRegistration, false),
_buildUploadButton( _buildUploadButton('Pesticide License', value.pesticideLicense, false),
'GST Registration', value.gstRegistration, false), _buildUploadButton('Fertilizer License', value.fertilizerLicense, true),
_buildUploadButton( _buildUploadButton('Selfie of Entrance Board', value.selfieEntranceBoard, false),
'Pesticide License', value.pesticideLicense, false),
_buildUploadButton(
'Fertilizer License', value.fertilizerLicense, true),
_buildUploadButton('Selfie of Entrance Board',
value.selfieEntranceBoard, false),
const SizedBox(height: 30), const SizedBox(height: 30),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
@ -108,11 +109,11 @@ class UploadDocumentScreenState extends State<UploadDocumentScreen> {
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'SUBMIT', text: 'SUBMIT', // Submit button
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
// Handle form submission // Handle form submission
value.tabController.animateTo(2); value.tabController.animateTo(2); // Navigate to the next tab
}, },
), ),
), ),

View File

@ -12,90 +12,87 @@ class VerifyDocumentsScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Drawer for navigation
body: CommonBackground( body: CommonBackground( // Background widget for styling
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(), // Enable bouncing effect on scroll
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Consumer<CollectKycProvider>( // Section for retailer details
Consumer<CollectKycProvider>( // Listen to changes in the KYC provider
builder: (BuildContext context, CollectKycProvider value, builder: (BuildContext context, CollectKycProvider value,
Widget? child) => Widget? child) =>
_buildSection( _buildSection(
'Retailer Details', 'Retailer Details', // Title for the section
{ {
'Trade Name': value.tradeNameController.text, 'Trade Name': value.tradeNameController.text,
'Name': value.nameController.text, 'Name': value.nameController.text,
'Address': value.addressController.text, 'Address': value.addressController.text,
'Town/City': value.city.text, 'Town/City': value.city.text,
'District': value.districtController.text, 'District': value.districtController.text,
'State': value.state.text, 'State': value.state.text,
'Pincode': value.pinCodeController.text, 'Pincode': value.pinCodeController.text,
'Mobile Number': value.mobileNumberController.text, 'Mobile Number': value.mobileNumberController.text,
'Aadhar Number': value.aadharNumberController.text, 'Aadhar Number': value.aadharNumberController.text,
'PAN Number': value.panNumberController.text, 'PAN Number': value.panNumberController.text,
'GST Number': value.gstNumberController.text, 'GST Number': value.gstNumberController.text,
'Mapped Principal Distributor': 'Mapped Principal Distributor':
value.selectedDistributor ?? '', value.selectedDistributor ?? '',
}, },
onEdit: () { onEdit: () {
value.tabController.animateTo(0); value.tabController.animateTo(0); // Navigate to the first tab for editing
// Handle edit for retailer details debugPrint('Edit retailer details'); // Debug message for tracking
debugPrint('Edit retailer details'); },
}, ),
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Consumer<CollectKycProvider>( // Section for uploaded documents
Consumer<CollectKycProvider>( // Listen to changes in the KYC provider
builder: (BuildContext context, CollectKycProvider value, builder: (BuildContext context, CollectKycProvider value,
Widget? child) => Widget? child) =>
_buildSection( _buildSection(
'Uploaded Documents', 'Uploaded Documents', // Title for the section
{ {
'PAN Card': 'PAN Card':
value.panCard?.path.split('/').last ?? 'Not uploaded', value.panCard?.path.split('/').last ?? 'Not uploaded',
'Aadhar Card': value.aadharCard?.path.split('/').last ?? 'Aadhar Card': value.aadharCard?.path.split('/').last ??
'Not uploaded', 'Not uploaded',
'GST Registration': 'GST Registration':
value.gstRegistration?.path.split('/').last ?? value.gstRegistration?.path.split('/').last ??
'Not uploaded', 'Not uploaded',
'Pesticide License': 'Pesticide License':
value.pesticideLicense?.path.split('/').last ?? value.pesticideLicense?.path.split('/').last ??
'Not uploaded', 'Not uploaded',
'Fertilizer License': 'Fertilizer License':
value.fertilizerLicense?.path.split('/').last ?? value.fertilizerLicense?.path.split('/').last ??
'Not uploaded', 'Not uploaded',
'Selfie of Entrance Board': 'Selfie of Entrance Board':
value.selfieEntranceBoard?.path.split('/').last ?? value.selfieEntranceBoard?.path.split('/').last ??
'Not uploaded', 'Not uploaded',
}, },
onEdit: () { onEdit: () {
value.tabController.animateTo(1); value.tabController.animateTo(1); // Navigate to the second tab for editing
debugPrint('Edit uploaded documents'); // Debug message for tracking
// Handle edit for uploaded documents },
debugPrint('Edit uploaded documents'); ),
},
),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
// Button to save and confirm the details
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Consumer<CollectKycProvider>( child: Consumer<CollectKycProvider>( // Listen to changes in the KYC provider
builder: (context, value, child) => CommonElevatedButton( builder: (context, value, child) => CommonElevatedButton(
borderRadius: 30, borderRadius: 30,
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: ('SAVE AND CONFIRM'), text: 'SAVE AND CONFIRM', // Button text
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
onPressed: () { onPressed: () {
// Handle final submission // Handle final submission of KYC details
value.validateFields(context); // Validate fields before submission
value.validateFields(context);
}, },
), ),
), ),
@ -108,14 +105,14 @@ class VerifyDocumentsScreen extends StatelessWidget {
); );
} }
// Method to build each section of the screen
Widget _buildSection(String title, Map<String, String> details, Widget _buildSection(String title, Map<String, String> details,
{required VoidCallback onEdit}) { {required VoidCallback onEdit}) {
return Container( return Container(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
// margin: const EdgeInsets.symmetric(horizontal: 30.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), 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), borderRadius: BorderRadius.circular(24.0),
), ),
child: Column( child: Column(
@ -125,24 +122,25 @@ class VerifyDocumentsScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
title, title, // Title of the section
style: style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
ElevatedButton( ElevatedButton(
onPressed: onEdit, onPressed: onEdit, // Trigger edit function
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xffFFFFFF), backgroundColor: const Color(0xffFFFFFF), // White background for the edit button
elevation: 0, elevation: 0,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
), ),
child: const Text('Edit'), child: const Text('Edit'), // Edit button text
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
// Build detail rows from the provided details map
...details.entries ...details.entries
.map((entry) => _buildDetailRow(entry.key, entry.value)), .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) { Widget _buildDetailRow(String label, String value) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0), padding: const EdgeInsets.symmetric(vertical: 5.0),
@ -159,16 +158,16 @@ class VerifyDocumentsScreen extends StatelessWidget {
Expanded( Expanded(
flex: 2, flex: 2,
child: Text( child: Text(
label, label, // Label for the detail
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
), ),
Expanded( Expanded(
flex: 3, flex: 3,
child: Text( child: Text(
value, value, // Value for the detail
style: TextStyle( style: TextStyle(
color: value == 'Not uploaded' ? Colors.red : Colors.black), color: value == 'Not uploaded' ? Colors.red : Colors.black), // Change color if not uploaded
)), )),
], ],
), ),

View File

@ -60,6 +60,7 @@ class _VerifyCodeScreenState extends State<VerifyCodeScreen> {
), ),
); );
// Function to submit the entered OTP
void _submitOtp() { void _submitOtp() {
if (_enteredOtp == "123456") { if (_enteredOtp == "123456") {
Navigator.push( Navigator.push(
@ -123,6 +124,7 @@ class _VerifyCodeScreenState extends State<VerifyCodeScreen> {
), ),
), ),
const Align( const Align(
// Message prompting the user to verify OTP
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
'OTP has sent to your registered \nmobile number, Please verify', 'OTP has sent to your registered \nmobile number, Please verify',
@ -135,6 +137,7 @@ class _VerifyCodeScreenState extends State<VerifyCodeScreen> {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Input field for OTP
Pinput( Pinput(
onTapOutside: (event) => FocusScope.of(context).unfocus(), onTapOutside: (event) => FocusScope.of(context).unfocus(),
length: 6, length: 6,
@ -147,6 +150,7 @@ class _VerifyCodeScreenState extends State<VerifyCodeScreen> {
onCompleted: (pin) => print(pin), onCompleted: (pin) => print(pin),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Countdown timer for resending OTP
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
@ -160,6 +164,7 @@ class _VerifyCodeScreenState extends State<VerifyCodeScreen> {
), ),
), ),
const SizedBox(height: 40), const SizedBox(height: 40),
// Button to verify the entered OTP
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: CommonElevatedButton( child: CommonElevatedButton(

View File

@ -4,9 +4,9 @@ import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../widgets/common_text_form_field.dart'; import '../widgets/common_text_form_field.dart';
// Main widget for the phone number verification screen
class VerifyPhoneScreen extends StatefulWidget { class VerifyPhoneScreen extends StatefulWidget {
const VerifyPhoneScreen({super.key}); const VerifyPhoneScreen({super.key});
@ -17,16 +17,18 @@ class VerifyPhoneScreen extends StatefulWidget {
class _VerifyPhoneScreenState extends State<VerifyPhoneScreen> { class _VerifyPhoneScreenState extends State<VerifyPhoneScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Builds the phone verification screen
return CommonBackground( return CommonBackground(
isFullWidth: false, isFullWidth: false,
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: AppBar( appBar: AppBar(
leading: InkWell( leading: InkWell(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context), // Navigate back
child: Image.asset('assets/Back_attendance.png')), child: Image.asset('assets/Back_attendance.png'),
backgroundColor: Colors.transparent), ),
backgroundColor: Colors.transparent,
),
body: Center( body: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Container( child: Container(
@ -40,44 +42,47 @@ class _VerifyPhoneScreenState extends State<VerifyPhoneScreen> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// Phone icon
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Image.asset( child: Image.asset(
'assets/phone.png', 'assets/phone.png',
height: 50.0, // Adjust the height as needed height: 50.0,
width: 50.0, // Adjust the width as needed width: 50.0,
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text('Verify Phone Number', // Title for the screen
style: TextStyle( const Text(
fontSize: 30, 'Verify Phone Number',
color: Colors.black, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 30,
fontFamily: 'Anek', color: Colors.black,
)), fontWeight: FontWeight.w500,
const SizedBox( fontFamily: 'Anek',
height: 10, ),
), ),
const Align( const SizedBox(height: 10),
alignment: Alignment.topLeft, // Instructional text
child: Text( const Text(
'Please enter your phone number\nto receive one time password', 'Please enter your phone number\nto receive one time password',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek', fontFamily: 'Anek',
)), ),
), ),
const SizedBox(height: 25), const SizedBox(height: 25),
// Input field for mobile number
CommonTextFormField( CommonTextFormField(
title: ' Enter Your Mobile Number', title: ' Enter Your Mobile Number',
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
keyboardType: TextInputType.number), keyboardType: TextInputType.number,
),
const SizedBox(height: 60), const SizedBox(height: 60),
// Button to submit phone number and request OTP
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: CommonElevatedButton( child: CommonElevatedButton(
@ -87,13 +92,14 @@ class _VerifyPhoneScreenState extends State<VerifyPhoneScreen> {
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'GET OTP', text: 'GET OTP',
onPressed: () { onPressed: () {
// Navigate to OTP screen
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const VerifyCodeScreen(),
const VerifyCodeScreen())); ),
// Handle OTP submission here );
print('OTP submitted'); print('OTP submitted'); // Debugging line
}, },
), ),
), ),

View File

@ -2,6 +2,7 @@ import 'package:cheminova/screens/home_screen.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// Screen displayed after successful verification
class VerifySuccessFullScreen extends StatefulWidget { class VerifySuccessFullScreen extends StatefulWidget {
const VerifySuccessFullScreen({super.key}); const VerifySuccessFullScreen({super.key});
@ -13,26 +14,39 @@ class _VerifySuccessFullScreenState extends State<VerifySuccessFullScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Navigate to the home screen after a delay of 2 seconds
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement( Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage())); context, MaterialPageRoute(builder: (context) => const HomePage()));
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( // Build the verification success screen
body: CommonBackground( return Scaffold(
child: Center( body: CommonBackground(
child: Column( child: Center(
mainAxisAlignment: MainAxisAlignment.center, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
const Text('Verification successful', children: [
style: TextStyle( // Display success message
fontSize: 36, const Text(
color: Colors.white, 'Verification successful',
fontWeight: FontWeight.w400, style: TextStyle(
fontFamily: 'Anek')), fontSize: 36,
const SizedBox(height: 20), // Add some space between the text and the image color: Colors.white,
Image.asset('assets/check_circle.png', )])))); 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'),
],
),
),
),
);
} }
} }

View File

@ -2,6 +2,7 @@ import 'package:cheminova/models/product_manual_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_cached_pdfview/flutter_cached_pdfview.dart'; import 'package:flutter_cached_pdfview/flutter_cached_pdfview.dart';
// Screen to display a PDF document for a product manual
class ViewPdfScreen extends StatefulWidget { class ViewPdfScreen extends StatefulWidget {
final ProductManualModel productManualModel; final ProductManualModel productManualModel;
@ -14,17 +15,23 @@ class ViewPdfScreen extends StatefulWidget {
class _ViewPdfScreenState extends State<ViewPdfScreen> { class _ViewPdfScreenState extends State<ViewPdfScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Build the PDF viewing screen
return Scaffold( return Scaffold(
body: SafeArea( body: SafeArea(
child: const PDF( // Use the PDF widget to display the PDF document
fitEachPage: true, child: const PDF(
fitPolicy: FitPolicy.BOTH, fitEachPage: true, // Fit the PDF content to each page
autoSpacing: false) fitPolicy: FitPolicy.BOTH, // Adjust both width and height
.cachedFromUrl( autoSpacing: false, // Disable auto spacing
widget.productManualModel.productManualDetail.url, ).cachedFromUrl(
placeholder: (progress) => // Fetch and cache the PDF from the provided URL
Center(child: Text('$progress %')), widget.productManualModel.productManualDetail.url,
errorWidget: (error) => placeholder: (progress) =>
Center(child: Text(error.toString()))))); Center(child: Text('$progress %')), // Display loading progress
errorWidget: (error) =>
Center(child: Text(error.toString())), // Display error message
),
),
);
} }
} }

View File

@ -9,10 +9,12 @@ import '../widgets/common_elevated_button.dart';
import '../widgets/common_text_form_field.dart'; import '../widgets/common_text_form_field.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
// Screen for visiting dealers and submitting visit details
class VisitDealersScreen extends StatefulWidget { class VisitDealersScreen extends StatefulWidget {
final String? tradeName; final String? tradeName; // Name of the trade
final String? id; final String? id; // ID of the visit
final String? type; final String? type; // Type of the visit
const VisitDealersScreen({super.key, required this.tradeName, this.id, this.type}); const VisitDealersScreen({super.key, required this.tradeName, this.id, this.type});
@override @override
@ -20,37 +22,37 @@ class VisitDealersScreen extends StatefulWidget {
} }
class VisitDealersScreenState extends State<VisitDealersScreen> { class VisitDealersScreenState extends State<VisitDealersScreen> {
late VisitPdRdProvider _visitPdRdProvider; late VisitPdRdProvider _visitPdRdProvider; // Provider for managing visit data
final dateController = TextEditingController( 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 = 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 notesController = TextEditingController(); // Controller for notes
final dealerController = TextEditingController(); final dealerController = TextEditingController(); // Controller for dealer input
final meetingSummaryController = TextEditingController(); final meetingSummaryController = TextEditingController(); // Controller for meeting summary
final followUpActionsController = TextEditingController(); final followUpActionsController = TextEditingController(); // Controller for follow-up actions
final nextVisitDateController = TextEditingController(); final nextVisitDateController = TextEditingController(); // Controller for next visit date
late TextEditingController retailerController = TextEditingController(); late TextEditingController retailerController = TextEditingController(); // Controller for retailer input
String selectedPurpose = 'Sales'; String selectedPurpose = 'Sales'; // Default selected purpose for the visit
List<String> purposeOptions = ['Sales', 'Dues collection', 'Others']; List<String> purposeOptions = ['Sales', 'Dues collection', 'Others']; // Options for visit purpose
// Function to pick an image from the camera or gallery
Future<void> _pickImage(ImageSource source) async { Future<void> _pickImage(ImageSource source) async {
final ImagePicker picker = ImagePicker(); 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) { if (image != null) {
// Handle the picked image // Handle the picked image
// For example, you could update a state variable or send it to your provider print('Image picked: ${image.path}'); // Log the picked image path
print('Image picked: ${image.path}');
// You might want to update your UI to show the selected image
setState(() { 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() { void _showImageSourceActionSheet() {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -59,19 +61,19 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
leading: const Icon(Icons.camera_alt), leading: const Icon(Icons.camera_alt), // Icon for camera
title: const Text('Take a photo'), title: const Text('Take a photo'), // Option to take a photo
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context); // Close the bottom sheet
_pickImage(ImageSource.camera); _pickImage(ImageSource.camera); // Pick image from camera
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.photo_library), leading: const Icon(Icons.photo_library), // Icon for gallery
title: const Text('Choose from gallery'), title: const Text('Choose from gallery'), // Option to choose from gallery
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context); // Close the bottom sheet
_pickImage(ImageSource.gallery); _pickImage(ImageSource.gallery); // Pick image from gallery
}, },
), ),
], ],
@ -81,43 +83,43 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
); );
} }
// Function to select the next visit date
Future<void> _selectNextVisitDate() async { Future<void> _selectNextVisitDate() async {
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
context: context, context: context,
initialDate: DateTime.now(), initialDate: DateTime.now(), // Set initial date to today
firstDate: DateTime.now(), firstDate: DateTime.now(), // Allow selecting today or later
lastDate: DateTime.now().add(const Duration(days: 365)), lastDate: DateTime.now().add(const Duration(days: 365)), // Limit selection to one year from today
); );
if (picked != null) { if (picked != null) {
setState(() { setState(() {
nextVisitDateController.text = DateFormat('dd/MM/yyyy').format(picked); nextVisitDateController.text = DateFormat('dd/MM/yyyy').format(picked); // Update controller with selected date
}); });
} }
} }
@override @override
void initState() { void initState() {
_visitPdRdProvider = VisitPdRdProvider(); _visitPdRdProvider = VisitPdRdProvider(); // Initialize provider
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
retailerController = TextEditingController(text: widget.tradeName); retailerController = TextEditingController(text: widget.tradeName); // Initialize retailer controller with trade name
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => _visitPdRdProvider, create: (context) => _visitPdRdProvider, // Provide visit data to the widget tree
child: CommonBackground( child: CommonBackground(
child: Stack( child: Stack(
children: [ children: [
Scaffold( Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context); // Back navigation
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
@ -131,7 +133,7 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Anek')), fontFamily: 'Anek')),
Text(widget.tradeName??'', Text(widget.tradeName ?? '', // Display trade name
style: const TextStyle( style: const TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
@ -142,7 +144,7 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
), ),
drawer: const CommonDrawer(), drawer: const CommonDrawer(), // Drawer for navigation
body: SingleChildScrollView( body: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
child: Column( child: Column(
@ -165,19 +167,19 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
readOnly: true, readOnly: true,
title: 'Select Retailer', title: 'Select Retailer',
fillColor: Colors.white, fillColor: Colors.white,
controller: retailerController), controller: retailerController), // Retailer selection field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Visit date', title: 'Visit date',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: dateController), controller: dateController), // Date field
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Time', title: 'Time',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: timeController), controller: timeController), // Time field
const SizedBox(height: 15), const SizedBox(height: 15),
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
decoration: const InputDecoration( decoration: const InputDecoration(
@ -185,7 +187,7 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
fillColor: Colors.white, fillColor: Colors.white,
filled: true, filled: true,
), ),
value: selectedPurpose, value: selectedPurpose, // Current selected purpose
items: purposeOptions.map((String value) { items: purposeOptions.map((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: value, value: value,
@ -194,7 +196,7 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
}).toList(), }).toList(),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
selectedPurpose = newValue!; selectedPurpose = newValue!; // Update selected purpose
}); });
}, },
), ),
@ -203,22 +205,22 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
title: 'Meeting summary:', title: 'Meeting summary:',
fillColor: Colors.white, fillColor: Colors.white,
maxLines: 3, maxLines: 3,
controller: meetingSummaryController), controller: meetingSummaryController), // Summary of the meeting
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Follow-up Actions:', title: 'Follow-up Actions:',
fillColor: Colors.white, fillColor: Colors.white,
maxLines: 3, maxLines: 3,
controller: followUpActionsController), controller: followUpActionsController), // Actions to follow up
const SizedBox(height: 15), const SizedBox(height: 15),
GestureDetector( GestureDetector(
onTap: _selectNextVisitDate, onTap: _selectNextVisitDate, // Handle date selection
child: AbsorbPointer( child: AbsorbPointer(
child: CommonTextFormField( child: CommonTextFormField(
title: 'Next visit date:', title: 'Next visit date:',
readOnly: true, readOnly: true,
fillColor: Colors.white, fillColor: Colors.white,
controller: nextVisitDateController, controller: nextVisitDateController, // Next visit date field
), ),
), ),
), ),
@ -229,25 +231,29 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
child: CommonTextFormField( child: CommonTextFormField(
title: 'Attach Documents/Photos', title: 'Attach Documents/Photos',
fillColor: Colors.white, fillColor: Colors.white,
controller: notesController), controller: notesController), // Notes for documents/photos
), ),
IconButton( IconButton(
icon: const Icon(Icons.add_a_photo), icon: const Icon(Icons.add_a_photo),
onPressed: _showImageSourceActionSheet, onPressed: _showImageSourceActionSheet, // Open image source selection
), ),
], ],
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
Consumer<VisitPdRdProvider>(builder: (context, value, child) => Align( Consumer<VisitPdRdProvider>(
alignment: Alignment.center, builder: (context, value, child) => Align(
child: CommonElevatedButton( alignment: Alignment.center,
borderRadius: 30, child: CommonElevatedButton(
width: double.infinity, borderRadius: 30,
height: kToolbarHeight - 10, width: double.infinity,
text: 'SUBMIT', height: kToolbarHeight - 10,
backgroundColor: const Color(0xff004791), text: 'SUBMIT', // Submit button
onPressed: () { backgroundColor: const Color(0xff004791),
value.submitVisitPdRd(widget.id ?? '', type: widget.type, onPressed: () {
// Submit visit details to provider
value.submitVisitPdRd(
widget.id ?? '',
type: widget.type,
retailerName: retailerController.text, retailerName: retailerController.text,
visitDate: dateController.text, visitDate: dateController.text,
visitTime: timeController.text, visitTime: timeController.text,
@ -255,11 +261,11 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
selectedPurpose: selectedPurpose, selectedPurpose: selectedPurpose,
followUpActions: followUpActionsController.text, followUpActions: followUpActionsController.text,
nextVisitDate: nextVisitDateController.text, nextVisitDate: nextVisitDateController.text,
); );
}, },
),
), ),
), ),
),
], ],
), ),
), ),
@ -267,20 +273,23 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
), ),
), ),
), ),
Consumer<VisitPdRdProvider>(builder: (context, value, child) { // Loading overlay
if (value.isLoading) { Consumer<VisitPdRdProvider>(
return Container( builder: (context, value, child) {
color: Colors.black.withOpacity(0.5), if (value.isLoading) {
child: const Center( return Container(
child: CircularProgressIndicator(), color: Colors.black.withOpacity(0.5),
), child: const Center(
); child: CircularProgressIndicator(), // Loading indicator
} ),
return const SizedBox.shrink(); );
}), }
return const SizedBox.shrink(); // Return empty widget when not loading
},
),
], ],
), ),
), ),
); );
} }
} }

View File

@ -4,46 +4,57 @@ import 'package:cheminova/services/api_urls.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart';
// ApiClient class for making HTTP requests using Dio
class ApiClient { class ApiClient {
final Dio _dio; final Dio _dio; // Instance of Dio for making HTTP requests
final SecureStorageService _storageService = SecureStorageService(); final SecureStorageService _storageService = SecureStorageService(); // Service for secure storage of tokens
// Constructor for ApiClient
ApiClient({String? baseUrl}) ApiClient({String? baseUrl})
: _dio = Dio(BaseOptions( : _dio = Dio(BaseOptions(
baseUrl: baseUrl ?? ApiUrls.baseUrl, baseUrl: baseUrl ?? ApiUrls.baseUrl, // Set base URL for API
connectTimeout: const Duration(seconds: 120), connectTimeout: const Duration(seconds: 120), // Timeout for connection
receiveTimeout: const Duration(seconds: 120))) { receiveTimeout: const Duration(seconds: 120))) { // Timeout for receiving data
// Adding interceptors for logging requests and responses
_dio.interceptors _dio.interceptors
.add(LogInterceptor(responseBody: true, requestBody: true)); .add(LogInterceptor(responseBody: true, requestBody: true)); // Log requests and responses
_dio.interceptors.add(PrettyDioLogger()); _dio.interceptors.add(PrettyDioLogger()); // Pretty logging for requests and responses
// Adding an interceptor for request handling
_dio.interceptors _dio.interceptors
.add(InterceptorsWrapper(onRequest: (options, handler) async { .add(InterceptorsWrapper(onRequest: (options, handler) async {
// Retrieve access token from secure storage
String? token = await _storageService.read(key: 'access_token'); String? token = await _storageService.read(key: 'access_token');
if (token != null) { if (token != null) {
debugPrint('Token start ------------> $token <------------ Token end'); debugPrint('Token start ------------> $token <------------ Token end'); // Debug log for token
options.headers['Authorization'] = 'Bearer $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) { }, onResponse: (response, handler) {
return handler.next(response); return handler.next(response); // Proceed to the next interceptor or response
}, onError: (DioException e, handler) { }, onError: (DioException e, handler) {
return handler.next(e); return handler.next(e); // Handle errors and proceed
})); }));
} }
// Method for making GET requests
Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) { Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) {
return _dio.get(path, queryParameters: queryParameters); return _dio.get(path, queryParameters: queryParameters); // Send GET request
} }
// Method for making POST requests
Future<Response> post(String path, {dynamic data}) { Future<Response> 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<Response> put(String path, {Map<String, dynamic>? data}) { Future<Response> put(String path, {Map<String, dynamic>? data}) {
return _dio.put(path, data: data); return _dio.put(path, data: data); // Send PUT request
} }
// Method for making DELETE requests
Future<Response> delete(String path, {Map<String, dynamic>? data}) { Future<Response> delete(String path, {Map<String, dynamic>? data}) {
return _dio.delete(path, data: data); return _dio.delete(path, data: data); // Send DELETE request
} }
} }