Submit VisitUrl Added
This commit is contained in:
parent
d077d06c07
commit
86a9b751ba
@ -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';
|
||||||
|
@ -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(),
|
||||||
|
@ -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(
|
||||||
|
@ -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,9 +34,10 @@ 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');
|
||||||
@ -42,17 +45,18 @@ class ProductProvider extends ChangeNotifier {
|
|||||||
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();
|
notifyListeners(); // Notify listeners to update the UI with the product list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,54 +97,62 @@ 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
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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.'),
|
||||||
|
@ -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,38 +131,39 @@ 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
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -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,139 +18,143 @@ 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(
|
||||||
|
'Change Password',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontFamily: 'Roboto')),
|
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)
|
|
||||||
.showSnackBar(SnackBar(
|
|
||||||
content:
|
|
||||||
Text(message)));
|
|
||||||
if (status) {
|
if (status) {
|
||||||
|
// Navigate to HomePage if password change is successful
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -162,7 +165,8 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -171,11 +175,14 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Overlay loading indicator when processing
|
||||||
Consumer<ChangePasswordProvider>(
|
Consumer<ChangePasswordProvider>(
|
||||||
builder: (context, value, child) => value.isLoading?
|
builder: (context, value, child) => value.isLoading
|
||||||
Container(
|
? Container(
|
||||||
color: Colors.black.withOpacity(0.5),
|
color: Colors.black.withOpacity(0.5), // Overlay background
|
||||||
child: const Center(child: CircularProgressIndicator())):Container(),
|
child: const Center(child: CircularProgressIndicator())) // Show loading indicator
|
||||||
|
: Container(),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -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,29 +145,28 @@ 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: const Text('Collect KYC Data', // Title for the AppBar
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@ -169,10 +174,9 @@ class CollectKycScreenState extends State<CollectKycScreen>
|
|||||||
fontFamily: 'Anek')),
|
fontFamily: 'Anek')),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
bottom: TabBar(
|
bottom: TabBar( // TabBar for navigation between the pages
|
||||||
controller: value.tabController,
|
controller: value.tabController, // Use the TabController from provider
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
horizontal: 10),
|
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
indicatorColor: Colors.yellow,
|
indicatorColor: Colors.yellow,
|
||||||
labelColor: Colors.white,
|
labelColor: Colors.white,
|
||||||
@ -180,21 +184,24 @@ class CollectKycScreenState extends State<CollectKycScreen>
|
|||||||
indicatorSize: TabBarIndicatorSize.tab,
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
indicator: BoxDecoration(
|
indicator: BoxDecoration(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
borderRadius: BorderRadius.circular(10)),
|
borderRadius: BorderRadius.circular(10)), // Custom styling for selected tab
|
||||||
tabs: const [
|
tabs: const [
|
||||||
Tab(text: 'Details'),
|
Tab(text: 'Details'), // First tab
|
||||||
Tab(text: 'Documents'),
|
Tab(text: 'Documents'), // Second tab
|
||||||
Tab(text: 'Verify')
|
Tab(text: 'Verify') // Third tab
|
||||||
]))),
|
]))),
|
||||||
drawer: const CommonDrawer(),
|
|
||||||
body: TabBarView(
|
drawer: const CommonDrawer(), // Drawer for navigation
|
||||||
|
body: TabBarView( // View for each tab
|
||||||
controller: value.tabController,
|
controller: value.tabController,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(), // Disable swipe gesture for tab switch
|
||||||
children: _pages)))),
|
children: _pages)))),
|
||||||
|
|
||||||
|
// Loading indicator overlay when `isLoading` is true
|
||||||
if (value.isLoading)
|
if (value.isLoading)
|
||||||
Container(
|
Container(
|
||||||
color: Colors.black.withOpacity(0.5),
|
color: Colors.black.withOpacity(0.5), // Semi-transparent overlay
|
||||||
child: const Center(child: CircularProgressIndicator()))
|
child: const Center(child: CircularProgressIndicator())) // Show loading spinner
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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');
|
||||||
@ -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,6 +165,7 @@ 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
|
||||||
@ -181,12 +190,14 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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()}');
|
||||||
|
// Navigation to respective screens based on task type
|
||||||
if (tasksList.task == 'Collect KYC') {
|
if (tasksList.task == 'Collect KYC') {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -216,7 +227,7 @@ class _DailyTasksScreenState extends State<DailyTasksScreen> {
|
|||||||
distributorType: tasksList.addedFor!,
|
distributorType: tasksList.addedFor!,
|
||||||
tradeName: tasksList.tradeName!,
|
tradeName: tasksList.tradeName!,
|
||||||
pdRdId: tasksList.addedForId!,
|
pdRdId: tasksList.addedForId!,
|
||||||
inventoryId:tasksList.sId,
|
inventoryId: tasksList.sId,
|
||||||
)));
|
)));
|
||||||
} else if (tasksList.task == 'Visit RD/PD') {
|
} else if (tasksList.task == 'Visit RD/PD') {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
@ -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) {
|
||||||
|
// Build the UI for the success screen
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: CommonBackground(
|
body: CommonBackground( // Use the custom background widget
|
||||||
child: Center(
|
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
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,19 +17,20 @@ 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) {
|
||||||
@ -36,9 +38,15 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
|
|||||||
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,18 +58,19 @@ 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(
|
||||||
@ -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(
|
||||||
|
'Back to Login',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontFamily: 'Roboto')),
|
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> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,8 +56,8 @@ 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)
|
||||||
@ -67,19 +68,20 @@ class _HomePageState extends State<HomePage> {
|
|||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
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
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,28 +13,28 @@ 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',
|
||||||
@ -43,32 +43,31 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
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(
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), // Styling for logo container
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Image.asset('assets/cheminova_logo.png',
|
child: Image.asset('assets/cheminova_logo.png', height: kToolbarHeight - 25), // Display logo
|
||||||
height: kToolbarHeight - 25),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 60),
|
const SizedBox(height: 60), // Spacer
|
||||||
Container(
|
Container(
|
||||||
padding:
|
padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), // Padding for login form
|
||||||
const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
|
margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50), // Margin for login form
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50),
|
|
||||||
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 for form container
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey, // Assign global key for form validation
|
||||||
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), // Spacer
|
||||||
const Text('Login',
|
const Text('Login',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
@ -81,46 +80,46 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
fontFamily: 'Roboto')),
|
fontFamily: 'Roboto')),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20), // Spacer
|
||||||
Consumer<LoginProvider>(
|
Consumer<LoginProvider>( // Listen for changes in LoginProvider
|
||||||
builder: (context, value, child) =>
|
builder: (context, value, child) =>
|
||||||
CommonTextFormField(
|
CommonTextFormField(
|
||||||
controller: value.emailController,
|
controller: value.emailController, // Email input field
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please enter your email id';
|
return 'Please enter your email id'; // Validation message
|
||||||
}
|
}
|
||||||
if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$')
|
if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$')
|
||||||
.hasMatch(value)) {
|
.hasMatch(value)) {
|
||||||
return 'Please enter a valid email id';
|
return 'Please enter a valid email id'; // Validation message for invalid email
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
title: 'Username')),
|
title: 'Username')), // Field title
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20), // Spacer
|
||||||
Consumer<LoginProvider>(
|
Consumer<LoginProvider>( // Listen for changes in LoginProvider
|
||||||
builder: (context, value, child) =>
|
builder: (context, value, child) =>
|
||||||
CommonTextFormField(
|
CommonTextFormField(
|
||||||
obscureText: true,
|
obscureText: true, // Hide password input
|
||||||
controller: value.passwordController,
|
controller: value.passwordController, // Password input field
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please enter your password';
|
return 'Please enter your password'; // Validation message
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
title: 'Password')),
|
title: 'Password')), // Field title
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15), // Spacer
|
||||||
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: TextButton(
|
child: TextButton( // Button for forgot password
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const ForgotPasswordScreen()));
|
const ForgotPasswordScreen())); // Navigate to forgot password screen
|
||||||
},
|
},
|
||||||
child: const Text('Forgot Password?',
|
child: const Text('Forgot Password?',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -130,42 +129,39 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
fontFamily: 'Roboto')),
|
fontFamily: 'Roboto')),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15), // Spacer
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Consumer<LoginProvider>(
|
child: Consumer<LoginProvider>( // Listen for changes in LoginProvider
|
||||||
builder: (context, value, child) =>
|
builder: (context, value, child) =>
|
||||||
CommonElevatedButton(
|
CommonElevatedButton(
|
||||||
backgroundColor: const Color(0xff004791),
|
backgroundColor: const Color(0xff004791), // Button color
|
||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
width: double.infinity,
|
width: double.infinity, // Full width button
|
||||||
height: kToolbarHeight - 10,
|
height: kToolbarHeight - 10,
|
||||||
text: 'SIGN IN',
|
text: 'SIGN IN', // Button text
|
||||||
isLoading: value.isLoading,
|
isLoading: value.isLoading, // Show loading indicator if true
|
||||||
// onPressed: () {
|
onPressed: value.isLoading // Disable button if loading
|
||||||
// Navigator.push(context, MaterialPageRoute(builder:(context) => const VerifyPhoneScreen()));
|
|
||||||
// },
|
|
||||||
onPressed: value.isLoading
|
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) { // Validate the form
|
||||||
value.login().then((result) {
|
value.login().then((result) { // Call login method
|
||||||
var (status, message) = result;
|
var (status, message) = result; // Destructure result
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(SnackBar(
|
.showSnackBar(SnackBar(
|
||||||
content: Text(message)));
|
content: Text(message))); // Show message in snackbar
|
||||||
if (status) {
|
if (status) {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const HomePage()));
|
const HomePage())); // Navigate to home screen if successful
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
))
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}, ),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -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),
|
||||||
|
@ -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,6 +22,7 @@ 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()));
|
||||||
|
|
||||||
@ -30,14 +32,16 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
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,14 +90,16 @@ 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));
|
||||||
@ -100,6 +107,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
|
|
||||||
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}';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -115,6 +123,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
// Navigate back 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'),
|
||||||
@ -151,12 +160,14 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
// Text field for date
|
||||||
CommonTextFormField(
|
CommonTextFormField(
|
||||||
title: 'Date',
|
title: 'Date',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
controller: dateController),
|
controller: dateController),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
// Text field for time
|
||||||
CommonTextFormField(
|
CommonTextFormField(
|
||||||
title: 'Time',
|
title: 'Time',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -170,6 +181,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontFamily: 'Anek')),
|
fontFamily: 'Anek')),
|
||||||
const SizedBox(height: 3),
|
const SizedBox(height: 3),
|
||||||
|
// Options for leave types
|
||||||
Wrap(
|
Wrap(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
@ -181,6 +193,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
// Text area for reason
|
||||||
CommonTextFormField(
|
CommonTextFormField(
|
||||||
height: 100,
|
height: 100,
|
||||||
title: 'Reason',
|
title: 'Reason',
|
||||||
@ -200,6 +213,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
backgroundColor:
|
backgroundColor:
|
||||||
const Color(0xff00784C),
|
const Color(0xff00784C),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
// Call the markLeave method when the button is pressed
|
||||||
value
|
value
|
||||||
.markLeave(
|
.markLeave(
|
||||||
dateController.text
|
dateController.text
|
||||||
@ -220,6 +234,7 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
content:
|
content:
|
||||||
Text(message)));
|
Text(message)));
|
||||||
if (status) {
|
if (status) {
|
||||||
|
// Navigate to success screen if leave is marked successfully
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -228,8 +243,6 @@ class _OnLeaveScreenState extends State<OnLeaveScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Navigator.push(context, MaterialPageRoute(builder:(context) => const AttendanceSuccess(),));
|
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
|
@ -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(
|
||||||
|
@ -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,6 +97,7 @@ 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(
|
||||||
@ -99,10 +106,13 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
|
|||||||
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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -110,7 +120,8 @@ class ProductPurchaseDataState extends State<ProductPurchaseData> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,7 +90,7 @@ 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
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,7 +34,7 @@ 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: [
|
||||||
@ -47,12 +49,12 @@ class ProfileScreen extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20), // Spacing for aesthetics
|
||||||
_buildProfileItem('Name', profileData?.name ?? ''),
|
_buildProfileItem('Name', profileData?.name ?? ''), // Display Name
|
||||||
_buildProfileItem('ID', profileData?.uniqueId ?? ''),
|
_buildProfileItem('ID', profileData?.uniqueId ?? ''), // Display User ID
|
||||||
_buildProfileItem('Email ID', profileData?.email ?? ''),
|
_buildProfileItem('Email ID', profileData?.email ?? ''), // Display Email
|
||||||
_buildProfileItem('Mobile Number', profileData?.mobileNumber ?? ''),
|
_buildProfileItem('Mobile Number', profileData?.mobileNumber ?? ''), // Display Mobile Number
|
||||||
_buildProfileItem('Designation', profileData?.designation ?? ''),
|
_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,7 +92,7 @@ class ProfileScreen extends StatelessWidget {
|
|||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(color: Colors.grey),
|
const Divider(color: Colors.grey), // Divider for visual separation
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -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
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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,
|
||||||
|
@ -16,13 +16,17 @@ 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
|
||||||
|
if (value != null) {
|
||||||
|
// If the token exists, navigate to the HomePage
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context, MaterialPageRoute(builder: (context) => const HomePage()));
|
context, MaterialPageRoute(builder: (context) => const HomePage()));
|
||||||
}else{
|
} else {
|
||||||
|
// If the token doesn't exist, navigate to the LoginPage
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context, MaterialPageRoute(builder: (context) => const LoginPage()));
|
context, MaterialPageRoute(builder: (context) => const LoginPage()));
|
||||||
}
|
}
|
||||||
@ -33,42 +37,54 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||||||
@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(
|
||||||
|
'HELPING YOU GROW', // Tagline text
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontFamily: robotoFamily,
|
fontFamily: robotoFamily,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500)),
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Image.asset('assets/plant.png'),
|
Image.asset('assets/plant.png'), // Image displayed at the center
|
||||||
const Column(
|
const Column(
|
||||||
children: [
|
children: [
|
||||||
Text('Powered By',
|
Text(
|
||||||
|
'Powered By', // Label indicating the provider
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: robotoFamily,
|
fontFamily: robotoFamily,
|
||||||
fontWeight: FontWeight.w500)),
|
fontWeight: FontWeight.w500,
|
||||||
Text('codeology.solutions',
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'codeology.solutions', // Company name
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontFamily: robotoFamily,
|
fontFamily: robotoFamily,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w700)),
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])));
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
'Summary',
|
||||||
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,
|
|
||||||
elevation: 0,
|
|
||||||
),
|
),
|
||||||
drawer: const CommonDrawer(),
|
backgroundColor: Colors.transparent, // Transparent app bar background
|
||||||
|
elevation: 0, // No shadow
|
||||||
|
),
|
||||||
|
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()));
|
||||||
|
},
|
||||||
})
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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: [
|
||||||
|
// 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('Update Inventory Data',
|
title: const Text('Update Inventory 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')), // Title of the app bar
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent, // Transparent app bar background
|
||||||
elevation: 0),
|
elevation: 0, // No shadow for the app bar
|
||||||
drawer: const CommonDrawer(),
|
),
|
||||||
body: Stack(children: [
|
drawer: const CommonDrawer(), // Drawer for navigation
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
Consumer<PdRdProvider>(
|
Consumer<PdRdProvider>(
|
||||||
builder: (context, value, child) => Column(children: [
|
builder: (context, value, child) => Column(
|
||||||
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25),
|
||||||
horizontal: 15.0, vertical: 25),
|
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
filled: true,
|
filled: true,
|
||||||
border: OutlineInputBorder()),
|
border: OutlineInputBorder()),
|
||||||
value: value.selectedDistributorType,
|
value: value.selectedDistributorType, // Currently selected distributor type
|
||||||
items: [
|
items: [
|
||||||
'Principal Distributor',
|
'Principal Distributor',
|
||||||
'Retail Distributor'
|
'Retail Distributor'
|
||||||
].map((String type) {
|
].map((String type) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: type, child: Text(type));
|
value: type, child: Text(type)); // Create dropdown items
|
||||||
}).toList(),
|
}).toList(),
|
||||||
hint: const Text('Select Distributor Type'),
|
hint: const Text('Select Distributor Type'), // Hint text
|
||||||
onChanged: (val) =>
|
onChanged: (val) =>
|
||||||
value.updateDistributorType(val))),
|
value.updateDistributorType(val), // Update selected distributor type
|
||||||
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 25),
|
||||||
horizontal: 15.0, vertical: 25),
|
|
||||||
child: DropdownButtonFormField<GetPdRdResponse>(
|
child: DropdownButtonFormField<GetPdRdResponse>(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
filled: true,
|
filled: true,
|
||||||
border: OutlineInputBorder()),
|
border: OutlineInputBorder()),
|
||||||
value: value.selectedPdRd,
|
value: value.selectedPdRd, // Currently selected distributor
|
||||||
items: value.pdRdList.map((e) {
|
items: value.pdRdList.map((e) {
|
||||||
return DropdownMenuItem<GetPdRdResponse>(
|
return DropdownMenuItem<GetPdRdResponse>(
|
||||||
value: e, child: Text(e.name ?? ''));
|
value: e, child: Text(e.name ?? '')); // Create dropdown items
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (val) =>
|
onChanged: (val) =>
|
||||||
value.updatePdRdValue(val),
|
value.updatePdRdValue(val), // Update selected distributor value
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
iconSize: 24,
|
iconSize: 24,
|
||||||
hint:
|
hint: const Text('Select Distributor Name'), // Hint text
|
||||||
const Text('Select Distributor Name')))
|
),
|
||||||
])),
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Consumer<PdRdProvider>(
|
Consumer<PdRdProvider>(
|
||||||
builder: (context, value, child) => value.isLoading
|
builder: (context, value, child) => value.isLoading
|
||||||
? Container(
|
? Container(
|
||||||
color: Colors.black12,
|
color: Colors.black12,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: CircularProgressIndicator()))
|
child: CircularProgressIndicator())) // Show loading indicator
|
||||||
: const SizedBox())
|
: 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
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -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,
|
||||||
@ -84,23 +89,19 @@ class UploadDocumentScreenState extends State<UploadDocumentScreen> {
|
|||||||
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
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -12,20 +12,21 @@ 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,
|
||||||
@ -42,18 +43,18 @@ class VerifyDocumentsScreen extends StatelessWidget {
|
|||||||
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',
|
||||||
@ -73,29 +74,25 @@ class VerifyDocumentsScreen extends StatelessWidget {
|
|||||||
'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
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
const Text(
|
||||||
|
'Verify Phone Number',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontFamily: 'Anek',
|
fontFamily: 'Anek',
|
||||||
)),
|
|
||||||
const SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
),
|
||||||
const Align(
|
),
|
||||||
alignment: Alignment.topLeft,
|
const SizedBox(height: 10),
|
||||||
child: Text(
|
// Instructional 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
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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) {
|
||||||
|
// Build the verification success screen
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: CommonBackground(
|
body: CommonBackground(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text('Verification successful',
|
// Display success message
|
||||||
|
const Text(
|
||||||
|
'Verification successful',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 36,
|
fontSize: 36,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontFamily: 'Anek')),
|
fontFamily: 'Anek',
|
||||||
const SizedBox(height: 20), // Add some space between the text and the image
|
),
|
||||||
Image.asset('assets/check_circle.png', )]))));
|
),
|
||||||
|
const SizedBox(height: 20), // Add space between the text and the image
|
||||||
|
// Display check circle image
|
||||||
|
Image.asset('assets/check_circle.png'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
// Use the PDF widget to display the PDF document
|
||||||
child: const PDF(
|
child: const PDF(
|
||||||
fitEachPage: true,
|
fitEachPage: true, // Fit the PDF content to each page
|
||||||
fitPolicy: FitPolicy.BOTH,
|
fitPolicy: FitPolicy.BOTH, // Adjust both width and height
|
||||||
autoSpacing: false)
|
autoSpacing: false, // Disable auto spacing
|
||||||
.cachedFromUrl(
|
).cachedFromUrl(
|
||||||
|
// Fetch and cache the PDF from the provided URL
|
||||||
widget.productManualModel.productManualDetail.url,
|
widget.productManualModel.productManualDetail.url,
|
||||||
placeholder: (progress) =>
|
placeholder: (progress) =>
|
||||||
Center(child: Text('$progress %')),
|
Center(child: Text('$progress %')), // Display loading progress
|
||||||
errorWidget: (error) =>
|
errorWidget: (error) =>
|
||||||
Center(child: Text(error.toString())))));
|
Center(child: Text(error.toString())), // Display error message
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>(
|
||||||
|
builder: (context, value, child) => 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: 'SUBMIT',
|
text: 'SUBMIT', // Submit button
|
||||||
backgroundColor: const Color(0xff004791),
|
backgroundColor: const Color(0xff004791),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
value.submitVisitPdRd(widget.id ?? '', type: widget.type,
|
// 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,
|
||||||
@ -267,17 +273,20 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Consumer<VisitPdRdProvider>(builder: (context, value, child) {
|
// Loading overlay
|
||||||
|
Consumer<VisitPdRdProvider>(
|
||||||
|
builder: (context, value, child) {
|
||||||
if (value.isLoading) {
|
if (value.isLoading) {
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.black.withOpacity(0.5),
|
color: Colors.black.withOpacity(0.5),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(), // Loading indicator
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink(); // Return empty widget when not loading
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user