Submit VisitUrl Added

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,168 +13,164 @@ class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
State<LoginPage> createState() => _LoginPageState(); // Create state for LoginPage
}
class _LoginPageState extends State<LoginPage> {
late LoginProvider loginProvider;
final _formKey = GlobalKey<FormState>();
late LoginProvider loginProvider; // Declare LoginProvider
final _formKey = GlobalKey<FormState>(); // Global key for form validation
@override
void initState() {
loginProvider = LoginProvider();
loginProvider = LoginProvider(); // Initialize LoginProvider
super.initState();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<LoginProvider>(
return ChangeNotifierProvider<LoginProvider>( // Provide LoginProvider to the widget tree
create: (_) => loginProvider,
builder: (context, child) => Scaffold(
body: CommonBackground(
isFullWidth: false,
child: Center(
child: SingleChildScrollView(
child: Column(
children: [
const Text('Welcome',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w400,
color: Colors.white,
fontFamily: 'Roboto')),
const SizedBox(height: 50),
Align(
alignment: Alignment.center,
child: Container(decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.all(8),
child: Image.asset('assets/cheminova_logo.png',
height: kToolbarHeight - 25),
),
),
const SizedBox(height: 60),
Container(
padding:
const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0)),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 20),
const Text('Login',
style: TextStyle(
fontSize: 30,
color: Colors.black,
fontWeight: FontWeight.w500,
fontFamily: 'Roboto')),
const Text('Sign in to continue',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w300,
fontFamily: 'Roboto')),
const SizedBox(height: 20),
Consumer<LoginProvider>(
builder: (context, value, child) =>
CommonTextFormField(
controller: value.emailController,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email id';
}
if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$')
.hasMatch(value)) {
return 'Please enter a valid email id';
}
return null;
},
title: 'Username')),
const SizedBox(height: 20),
Consumer<LoginProvider>(
builder: (context, value, child) =>
CommonTextFormField(
obscureText: true,
controller: value.passwordController,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
},
title: 'Password')),
const SizedBox(height: 15),
Align(
alignment: Alignment.center,
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ForgotPasswordScreen()));
},
child: const Text('Forgot Password?',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Roboto')),
),
),
const SizedBox(height: 15),
Align(
alignment: Alignment.center,
child: Consumer<LoginProvider>(
builder: (context, value, child) =>
CommonElevatedButton(
backgroundColor: const Color(0xff004791),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SIGN IN',
isLoading: value.isLoading,
// onPressed: () {
// Navigator.push(context, MaterialPageRoute(builder:(context) => const VerifyPhoneScreen()));
// },
onPressed: value.isLoading
? null
: () async {
if (_formKey.currentState!.validate()) {
value.login().then((result) {
var (status, message) = result;
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(message)));
if (status) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
const HomePage()));
}
});
}
},
),
))
],
isFullWidth: false,
child: Center(
child: SingleChildScrollView( // Allow scrolling for the content
child: Column(
children: [
const Text('Welcome',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w400,
color: Colors.white,
fontFamily: 'Roboto')),
const SizedBox(height: 50), // Spacer
Align(
alignment: Alignment.center,
child: Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), // Styling for logo container
padding: const EdgeInsets.all(8),
child: Image.asset('assets/cheminova_logo.png', height: kToolbarHeight - 25), // Display logo
),
),
),
const SizedBox(height: 60), // Spacer
Container(
padding: const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), // Padding for login form
margin: const EdgeInsets.symmetric(horizontal: 30.0).copyWith(bottom: 50), // Margin for login form
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9),
borderRadius: BorderRadius.circular(26.0)), // Styling for form container
child: Form(
key: _formKey, // Assign global key for form validation
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 20), // Spacer
const Text('Login',
style: TextStyle(
fontSize: 30,
color: Colors.black,
fontWeight: FontWeight.w500,
fontFamily: 'Roboto')),
const Text('Sign in to continue',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w300,
fontFamily: 'Roboto')),
const SizedBox(height: 20), // Spacer
Consumer<LoginProvider>( // Listen for changes in LoginProvider
builder: (context, value, child) =>
CommonTextFormField(
controller: value.emailController, // Email input field
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email id'; // Validation message
}
if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$')
.hasMatch(value)) {
return 'Please enter a valid email id'; // Validation message for invalid email
}
return null;
},
title: 'Username')), // Field title
const SizedBox(height: 20), // Spacer
Consumer<LoginProvider>( // Listen for changes in LoginProvider
builder: (context, value, child) =>
CommonTextFormField(
obscureText: true, // Hide password input
controller: value.passwordController, // Password input field
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password'; // Validation message
}
return null;
},
title: 'Password')), // Field title
const SizedBox(height: 15), // Spacer
Align(
alignment: Alignment.center,
child: TextButton( // Button for forgot password
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ForgotPasswordScreen())); // Navigate to forgot password screen
},
child: const Text('Forgot Password?',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Roboto')),
),
),
const SizedBox(height: 15), // Spacer
Align(
alignment: Alignment.center,
child: Consumer<LoginProvider>( // Listen for changes in LoginProvider
builder: (context, value, child) =>
CommonElevatedButton(
backgroundColor: const Color(0xff004791), // Button color
borderRadius: 30,
width: double.infinity, // Full width button
height: kToolbarHeight - 10,
text: 'SIGN IN', // Button text
isLoading: value.isLoading, // Show loading indicator if true
onPressed: value.isLoading // Disable button if loading
? null
: () async {
if (_formKey.currentState!.validate()) { // Validate the form
value.login().then((result) { // Call login method
var (status, message) = result; // Destructure result
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(message))); // Show message in snackbar
if (status) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
const HomePage())); // Navigate to home screen if successful
}
});
}
},
),
)),
],
),
),
),
],
),
],
),
),
),
),
)),
)),
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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