Compare commits

...

10 Commits

Author SHA1 Message Date
kratikpal
2d393a9885 Visit RD/PD and update sales task 2024-09-06 10:08:19 +05:30
kratikpal
ec339c48ba task status 2024-08-30 17:50:19 +05:30
kratikpal
12af640bae Assign task and Product manual 2024-08-29 10:30:47 +05:30
kratikpal
1436aa0131 assign task of KYC 2024-08-23 09:11:23 +05:30
kratikpal
7cce7db73c Validations on add product screen 2024-08-16 16:36:18 +05:30
kratikpal
aa04f39fca update inventory bug solved 2024-08-16 15:16:58 +05:30
kratikpal
60ae618071 Bug fix 2024-08-16 10:23:42 +05:30
kratikpal
5391d953a7 inventory update 2024-08-14 16:56:38 +05:30
kratikpal
de188372d9 Pd and Rd list added 2024-08-14 14:37:47 +05:30
kratikpal
7e86e93c8e add product updated 2024-08-14 10:12:26 +05:30
35 changed files with 2537 additions and 658 deletions

View File

@ -1,7 +1,10 @@
import 'dart:developer';
import 'dart:io';
import 'package:cheminova/provider/collect_kyc_provider.dart';
import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/provider/product_manual_provider.dart';
import 'package:cheminova/provider/product_provider.dart';
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/provider/user_provider.dart';
import 'package:cheminova/screens/splash_screen.dart';
import 'package:firebase_core/firebase_core.dart';
@ -20,8 +23,10 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
}
AndroidNotificationChannel channel = const AndroidNotificationChannel(
'High Importance Channel', 'High Importance Notifications',
importance: Importance.high);
'High Importance Channel',
'High Importance Notifications',
importance: Importance.high,
);
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
@ -98,6 +103,9 @@ Future<void> main() async {
ChangeNotifierProvider(create: (context) => CollectKycProvider()),
ChangeNotifierProvider(create: (_) => UserProvider()),
ChangeNotifierProvider(create: (_) => ProductProvider()),
ChangeNotifierProvider(create: (_) => PdRdProvider()),
ChangeNotifierProvider(create: (_) => TaskProvider()),
ChangeNotifierProvider(create: (_) => ProductManualProvider()),
],
child: const MyApp(),
),
@ -115,12 +123,16 @@ class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey,
title: 'cheminova',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true),
home: const SplashScreen());
debugShowCheckedModeBanner: false,
// scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey,
title: 'cheminova',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xff004791),
),
useMaterial3: true,
),
home: const SplashScreen(),
);
}
}

View File

@ -0,0 +1,72 @@
class PdRdResponseModel {
String? id;
String? uniqueId;
String? name;
String? tradeNameRd;
ShippingAddress? shippingAddress;
String? salesCoordinator; // Nullable property for SalesCoordinator
PdRdResponseModel({
this.id,
this.uniqueId,
this.name,
this.tradeNameRd,
this.shippingAddress,
this.salesCoordinator, // Initialize SalesCoordinator
});
factory PdRdResponseModel.fromJson(Map<String, dynamic> json) =>
PdRdResponseModel(
id: json["_id"],
name: json["name"],
uniqueId: json["uniqueId"],
tradeNameRd: json["trade_name"],
shippingAddress: json['shippingAddress'] != null
? ShippingAddress.fromJson(json['shippingAddress'])
: null,
salesCoordinator: json.containsKey('salesCoordinator')
? json['salesCoordinator']
: null, // Handle missing field
);
}
class ShippingAddress {
final String id;
final String street;
final String city;
final String state;
final String postalCode;
final String country;
final String panNumber;
final String tradeName;
final String gstNumber;
final bool isDefault;
ShippingAddress({
required this.id,
required this.street,
required this.city,
required this.state,
required this.postalCode,
required this.country,
required this.panNumber,
required this.tradeName,
required this.gstNumber,
required this.isDefault,
});
factory ShippingAddress.fromJson(Map<String, dynamic> json) {
return ShippingAddress(
id: json['_id'] ?? '',
street: json['street'] ?? '',
city: json['city'] ?? '',
state: json['state'] ?? '',
postalCode: json['postalCode'] ?? '',
country: json['country'] ?? '',
panNumber: json['panNumber'] ?? '',
tradeName: json['tradeName'] ?? '',
gstNumber: json['gstNumber'] ?? '',
isDefault: json['isDefault'] ?? false,
);
}
}

View File

@ -0,0 +1,48 @@
class ProductManualModel {
final ProductManualDetail productManualDetail;
final String id;
final String title;
final String createdAt;
final String updatedAt;
final int version;
ProductManualModel({
required this.productManualDetail,
required this.id,
required this.title,
required this.createdAt,
required this.updatedAt,
required this.version,
});
factory ProductManualModel.fromJson(Map<String, dynamic> json) {
return ProductManualModel(
productManualDetail: ProductManualDetail.fromJson(json['product_manual']),
id: json['_id'],
title: json['title'],
createdAt: json['createdAt'],
updatedAt: json['updatedAt'],
version: json['__v'],
);
}
}
class ProductManualDetail {
final String publicId;
final String url;
final String filename;
ProductManualDetail({
required this.publicId,
required this.url,
required this.filename,
});
factory ProductManualDetail.fromJson(Map<String, dynamic> json) {
return ProductManualDetail(
publicId: json['public_id'],
url: json['url'],
filename: json['filename'],
);
}
}

View File

@ -2,13 +2,13 @@ class ProductResponse {
final bool success;
final int totalData;
final int totalPages;
final List<Product> product;
final List<Product> products;
ProductResponse({
required this.success,
required this.totalData,
required this.totalPages,
required this.product,
required this.products,
});
factory ProductResponse.fromJson(Map<String, dynamic> json) {
@ -16,7 +16,7 @@ class ProductResponse {
success: json['success'],
totalData: json['total_data'],
totalPages: json['total_pages'],
product: (json['product'] as List)
products: (json['products'] as List)
.map((item) => Product.fromJson(item))
.toList(),
);
@ -27,7 +27,7 @@ class ProductResponse {
'success': success,
'total_data': totalData,
'total_pages': totalPages,
'product': product.map((item) => item.toJson()).toList(),
'products': products.map((item) => item.toJson()).toList(),
};
}
}
@ -37,32 +37,38 @@ class Product {
final String SKU;
final String name;
final Category category;
final Brand brand;
final double price;
final GST gst;
final int GST;
final int HSNCode;
final String description;
final String specialInstructions;
final String productStatus;
final AddedBy addedBy;
final List<dynamic> image;
final DateTime createdAt;
final DateTime updatedAt;
final int v;
int? sale;
int? inventory;
Product({
required this.id,
required this.SKU,
required this.name,
required this.category,
required this.brand,
required this.price,
required this.gst,
required this.GST,
required this.HSNCode,
required this.description,
required this.specialInstructions,
required this.productStatus,
required this.addedBy,
required this.image,
required this.createdAt,
required this.updatedAt,
required this.v,
this.sale,
this.inventory,
});
factory Product.fromJson(Map<String, dynamic> json) {
@ -71,16 +77,19 @@ class Product {
SKU: json['SKU'],
name: json['name'],
category: Category.fromJson(json['category']),
brand: Brand.fromJson(json['brand']),
price: (json['price'] as num).toDouble(),
gst: GST.fromJson(json['GST']),
description: json['description'],
specialInstructions: json['special_instructions'],
GST: json['GST'],
HSNCode: json['HSN_Code'],
description: json['description'] ?? '',
productStatus: json['product_Status'],
addedBy: AddedBy.fromJson(json['addedBy']),
image: json['image'] as List<dynamic>,
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
v: json['__v'],
sale: json['sale'], // Nullable field
inventory: json['inventory'], // Nullable field
);
}
@ -90,16 +99,19 @@ class Product {
'SKU': SKU,
'name': name,
'category': category.toJson(),
'brand': brand.toJson(),
'price': price,
'GST': gst.toJson(),
'GST': GST,
'HSN_Code': HSNCode,
'description': description,
'special_instructions': specialInstructions,
'product_Status': productStatus,
'addedBy': addedBy.toJson(),
'image': image,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
'__v': v,
'sale': sale, // Nullable field
'inventory': inventory, // Nullable field
};
}
}
@ -128,30 +140,26 @@ class Category {
}
}
class GST {
class Brand {
final String id;
final String name;
final int tax;
final String brandName;
GST({
Brand({
required this.id,
required this.name,
required this.tax,
required this.brandName,
});
factory GST.fromJson(Map<String, dynamic> json) {
return GST(
factory Brand.fromJson(Map<String, dynamic> json) {
return Brand(
id: json['_id'],
name: json['name'],
tax: json['tax'],
brandName: json['brandName'],
);
}
Map<String, dynamic> toJson() {
return {
'_id': id,
'name': name,
'tax': tax,
'brandName': brandName,
};
}
}

View File

@ -0,0 +1,78 @@
class TaskModel {
final String id;
final String taskId;
final String task;
final String? note;
final String taskStatus;
final String? taskPriority;
final DateTime taskDueDate;
final TaskAssignedTo taskAssignedTo;
final String? taskAssignedBy;
final String? addedFor;
final String? addedForId;
final String? tradename;
final DateTime? createdAt;
final DateTime? updatedAt;
final int? version;
TaskModel({
required this.id,
required this.taskId,
required this.task,
this.note,
required this.taskStatus,
this.taskPriority,
required this.taskDueDate,
required this.taskAssignedTo,
this.taskAssignedBy,
this.addedFor,
this.addedForId,
this.tradename,
this.createdAt,
this.updatedAt,
this.version,
});
factory TaskModel.fromJson(Map<String, dynamic> json) {
return TaskModel(
id: json['_id'],
taskId: json['taskId'],
task: json['task'],
note: json['note'],
taskStatus: json['taskStatus'],
taskPriority: json['taskPriority'],
taskDueDate: DateTime.parse(json['taskDueDate']),
taskAssignedTo: TaskAssignedTo.fromJson(json['taskAssignedTo']),
taskAssignedBy: json['taskAssignedBy'],
addedFor: json['addedFor'],
addedForId: json['addedForId'],
tradename: json['tradename'],
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
version: json['__v'],
);
}
}
class TaskAssignedTo {
final String id;
final String name;
final String mobileNumber;
final String email;
TaskAssignedTo({
required this.id,
required this.name,
required this.mobileNumber,
required this.email,
});
factory TaskAssignedTo.fromJson(Map<String, dynamic> json) {
return TaskAssignedTo(
id: json['_id'],
name: json['name'],
mobileNumber: json['mobileNumber'],
email: json['email'],
);
}
}

View File

@ -3,6 +3,8 @@ class UserModel {
String name;
String email;
String uniqueId;
String mobileNumber;
String designation;
bool isVerified;
UserModel({
@ -10,6 +12,8 @@ class UserModel {
required this.name,
required this.email,
required this.uniqueId,
required this.mobileNumber,
required this.designation,
required this.isVerified,
});
@ -19,8 +23,9 @@ class UserModel {
name: json['name'],
email: json['email'],
uniqueId: json['uniqueId'],
mobileNumber: json['mobileNumber'],
designation: json['designation'],
isVerified: json['isVerified'],
);
}
}

View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/services/api_client.dart';
import 'package:cheminova/services/api_urls.dart';
class PdRdProvider extends ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
List<PdRdResponseModel> _pdList = [];
List<PdRdResponseModel> get pdList => _pdList;
List<PdRdResponseModel> _rdList = [];
List<PdRdResponseModel> get rdList => _rdList;
final _apiClient = ApiClient();
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
void clearLists() {
_pdList.clear();
_rdList.clear();
notifyListeners();
}
Future<void> getPd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getPd);
if (response.statusCode == 200) {
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_pdList = data;
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getRd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getRd);
if (response.statusCode == 200) {
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_rdList = data;
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
}

View File

@ -0,0 +1,39 @@
import 'package:cheminova/models/product_manual_model.dart';
import 'package:cheminova/services/api_urls.dart';
import 'package:cheminova/services/api_client.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class ProductManualProvider extends ChangeNotifier {
bool _isLoading = false;
List<ProductManualModel> _productManualList = [];
final _apiClient = ApiClient();
List<ProductManualModel> get productManualList => _productManualList;
bool get isLoading => _isLoading;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
Future<void> getProductManualList() async {
setLoading(true);
try {
Response response = await _apiClient.get(ApiUrls.getProductsManual);
if (response.statusCode == 200) {
List<ProductManualModel> data =
(response.data['productManuals'] as List)
.map((json) => ProductManualModel.fromJson(json))
.toList();
_productManualList = data;
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
}

View File

@ -12,11 +12,14 @@ class ProductProvider extends ChangeNotifier {
final _apiClient = ApiClient();
ProductResponse? productResponse;
List<Product> productList = [];
final List<Product> _selectedProducts = [];
bool _isLoading = false;
bool get isLoading => _isLoading;
List<Product> get selectedProducts => _selectedProducts;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
@ -29,7 +32,7 @@ class ProductProvider extends ChangeNotifier {
setLoading(false);
if (response.statusCode == 200) {
productResponse = ProductResponse.fromJson(response.data);
productList = productResponse!.product;
productList = productResponse!.products;
notifyListeners();
}
} catch (e) {
@ -37,4 +40,36 @@ class ProductProvider extends ChangeNotifier {
print("Error: $e");
}
}
Future<bool> submitSelectedProducts(
String addedFor, String addedForId) async {
setLoading(true);
try {
final data = {
"products": selectedProducts.map((product) {
return {
"SKU": product.SKU,
"ProductName": product.name,
"Sale": product.sale,
"Inventory": product.inventory,
};
}).toList(),
"addedFor": addedFor,
"addedForId": addedForId,
};
Response response =
await _apiClient.post(ApiUrls.submitProducts, data: data);
setLoading(false);
if (response.statusCode == 201) {
return true;
} else {
return false;
}
} catch (e) {
setLoading(false);
print("Error: $e");
return false;
}
}
}

View File

@ -0,0 +1,248 @@
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/models/task_model.dart';
import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:cheminova/services/api_client.dart';
import 'package:cheminova/services/api_urls.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class TaskProvider extends ChangeNotifier {
bool _isLoading = false;
PdRdResponseModel? _selectedSalesCoordinator;
String? _selectedTask;
String? _selectedPriority;
String _selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
List<PdRdResponseModel> _salesCoordinators = [];
List<PdRdResponseModel> _pdList = [];
List<PdRdResponseModel> _rdList = [];
List<TaskModel> _taskModelList = [];
PdRdResponseModel? _selectedDistributor;
final TextEditingController _noteController = TextEditingController();
final _apiClient = ApiClient();
bool get isLoading => _isLoading;
PdRdResponseModel? get selectedSalesCoordinator => _selectedSalesCoordinator;
String? get selectedTask => _selectedTask;
String? get selectedPriority => _selectedPriority;
String get selectedDate => _selectedDate;
List<PdRdResponseModel> get salesCoordinators => _salesCoordinators;
TextEditingController get noteController => _noteController;
List<PdRdResponseModel> get pdList => _pdList;
List<PdRdResponseModel> get rdList => _rdList;
List<TaskModel> get taskModelList => _taskModelList;
PdRdResponseModel? get selectedDistributor => _selectedDistributor;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
void setSelectedSalesCoordinator(PdRdResponseModel coordinator) {
_selectedSalesCoordinator = coordinator;
notifyListeners();
}
void setSelectedTask(String? task) {
_selectedTask = task;
notifyListeners();
}
void setSelectedPriority(String priority) {
_selectedPriority = priority;
notifyListeners();
}
void setDate(String date) {
_selectedDate = date;
notifyListeners();
}
void clear() {
_selectedSalesCoordinator = null;
_selectedTask = null;
_selectedPriority = null;
_selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
_noteController.clear();
_taskModelList.clear();
notifyListeners();
}
Future<void> getSalesCoordinators() async {
setLoading(true);
try {
Response response = await _apiClient.get(ApiUrls.getSalesCoordinators);
if (response.statusCode == 200) {
List<PdRdResponseModel> data =
(response.data['salesCoOrinators'] as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_salesCoordinators = data;
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
setLoading(false);
} finally {
setLoading(false);
}
}
Future<void> assignTask({
required BuildContext context,
String? selectedDistributorType,
}) async {
setLoading(true);
try {
final data = {
'taskAssignedTo': _selectedSalesCoordinator!.id,
'task': _selectedTask,
'taskPriority': _selectedPriority,
'taskDueDate': _selectedDate,
if (_selectedTask == 'Collect KYC' || _selectedTask == 'Visit RD/PD')
'note': _noteController.text,
};
if (selectedDistributorType != null &&
selectedDistributorType.isNotEmpty) {
data.addAll({
'addedFor': selectedDistributorType,
'addedForId': _selectedDistributor!.id,
'tradename': selectedDistributorType == 'PrincipalDistributor'
? _selectedDistributor!.shippingAddress!.tradeName
: _selectedDistributor!.tradeNameRd,
});
}
Response response = await _apiClient.post(
ApiUrls.assignTask,
data: data,
);
if (response.statusCode == 201) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull(),
),
);
} else {
print("Failed to assign task: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
void clearLists() {
_pdList.clear();
_rdList.clear();
notifyListeners();
}
void setSelectedDistributor(PdRdResponseModel? distributor) {
_selectedDistributor = distributor;
notifyListeners();
}
Future<void> getPd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getPd);
if (response.statusCode == 200) {
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_pdList = data;
print("PDTradeName ${data[0].shippingAddress!.tradeName}");
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getRd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getRd);
if (response.statusCode == 200) {
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_rdList = data;
print("RDTradeName ${data[0].tradeNameRd}");
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getNewTasks() async {
setLoading(true);
clearLists();
try {
Response response = await _apiClient.get("${ApiUrls.getAllTasks}New");
if (response.statusCode == 200) {
List<TaskModel> data = (response.data['tasks'] as List)
.map((json) => TaskModel.fromJson(json))
.toList();
_taskModelList = data;
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getPendingTasks() async {
setLoading(true);
clearLists();
try {
Response response = await _apiClient.get("${ApiUrls.getAllTasks}Pending");
if (response.statusCode == 200) {
List<TaskModel> data = (response.data['tasks'] as List)
.map((json) => TaskModel.fromJson(json))
.toList();
_taskModelList = data;
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getCompletedTasks() async {
setLoading(true);
clearLists();
try {
Response response =
await _apiClient.get("${ApiUrls.getAllTasks}Completed");
if (response.statusCode == 200) {
List<TaskModel> data = (response.data['tasks'] as List)
.map((json) => TaskModel.fromJson(json))
.toList();
_taskModelList = data;
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
}

View File

@ -0,0 +1,36 @@
import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../services/api_client.dart';
import '../services/api_urls.dart';
class VisitPdRdProvider with ChangeNotifier {
final _apiClient = ApiClient();
bool _isLoading = false;
bool get isLoading => _isLoading;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
Future<void> submitVisitPdRd(BuildContext context, String id) async {
setLoading(true);
try {
Response response =
await _apiClient.put(ApiUrls.updateTaskInventoryUrl + id);
debugPrint('Response: $response');
setLoading(false);
if (response.statusCode == 200) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull()));
}
} catch (e) {
setLoading(false);
debugPrint("Error: $e");
}
}
}

View File

@ -1,20 +1,25 @@
import 'package:cheminova/provider/visit_pdrd_provider.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:flutter/material.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../widgets/common_app_bar.dart';
import '../widgets/common_elevated_button.dart';
import '../widgets/common_text_form_field.dart';
import 'package:image_picker/image_picker.dart';
class VisitDealersScreen extends StatefulWidget {
const VisitDealersScreen({super.key});
final String? tradeName;
final String? id;
const VisitDealersScreen({super.key, this.tradeName, this.id});
@override
State<VisitDealersScreen> createState() => VisitDealersScreenState();
}
class VisitDealersScreenState extends State<VisitDealersScreen> {
late VisitPdRdProvider _visitPdRdProvider;
final dateController = TextEditingController(
text: DateFormat('dd/MM/yyyy').format(DateTime.now()));
@ -26,13 +31,10 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
final meetingSummaryController = TextEditingController();
final followUpActionsController = TextEditingController();
final nextVisitDateController = TextEditingController();
late TextEditingController retailerController = TextEditingController();
String selectedPurpose = 'Sales/Liquidation';
List<String> purposeOptions = [
'Sales/Liquidation',
'Dues collection',
'Others'
];
String selectedPurpose = 'Sales';
List<String> purposeOptions = ['Sales', 'Dues collection', 'Others'];
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
@ -40,34 +42,52 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
// Handle the picked image
}
@override
void initState() {
_visitPdRdProvider = VisitPdRdProvider();
super.initState();
}
@override
Widget build(BuildContext context) {
return 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('Visit Retailers',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
retailerController = TextEditingController(text: widget.tradeName);
return ChangeNotifierProvider(
create: (context) => _visitPdRdProvider,
child: CommonBackground(
child: Scaffold(
backgroundColor: Colors.transparent,
elevation: 0,
),
drawer: const CommonDrawer(),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
appBar: CommonAppBar(
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: Column(
children: [
const Text('Visit Retailers',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
Text(widget.tradeName ?? '',
style: const TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
],
),
backgroundColor: Colors.transparent,
elevation: 0,
),
drawer: const CommonDrawer(),
body: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -77,7 +97,7 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
Container(
padding:
const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30),
// margin: const EdgeInsets.symmetric(horizontal: 30.0),
margin: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.9),
@ -86,9 +106,10 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CommonTextFormField(
readOnly: true,
title: 'Select Retailer',
fillColor: Colors.white,
controller: dealerController),
controller: retailerController),
const SizedBox(height: 15),
CommonTextFormField(
title: 'Visit date',
@ -155,15 +176,19 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
],
),
const SizedBox(height: 15),
Align(
alignment: Alignment.center,
child: CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {},
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(context, widget.id ?? '');
},
),
),
),
],

View File

@ -1,3 +1,4 @@
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/models/product_model.dart';
import 'package:cheminova/provider/product_provider.dart';
import 'package:cheminova/screens/data_submit_successfull.dart';
@ -9,20 +10,36 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AddProductsScreen extends StatefulWidget {
const AddProductsScreen({super.key});
final PdRdResponseModel distributor;
final String distributorType;
const AddProductsScreen({
super.key,
required this.distributor,
required this.distributorType,
});
@override
State<AddProductsScreen> createState() => _AddProductsScreenState();
}
class _AddProductsScreenState extends State<AddProductsScreen> {
List<Product> selectedProducts = [];
List<Product> filteredProducts = [];
final searchController = TextEditingController();
late ProductProvider provider;
@override
void initState() {
super.initState();
loadProducts();
}
Future<void> loadProducts() async {
provider = Provider.of<ProductProvider>(context, listen: false);
await provider.getProducts();
setState(() {
provider.selectedProducts.clear();
filteredProducts = provider.productList;
});
}
void filterProducts(String query) {
@ -54,12 +71,15 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
padding: const EdgeInsets.only(right: 20),
),
],
title: const Text('Add Products',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
title: Text(
widget.distributor.name!,
style: const TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek',
),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
@ -70,26 +90,23 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
return const Center(child: CircularProgressIndicator());
}
filteredProducts = provider.productList;
return Stack(
children: [
Column(
children: [
if (selectedProducts.isNotEmpty)
if (provider.selectedProducts.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: selectedProducts.length,
itemCount: provider.selectedProducts.length,
itemBuilder: (context, index) {
return ProductBlock(
product: selectedProducts[index]);
return ProductBlock(index: index);
},
),
),
],
),
Align(
alignment: selectedProducts.isEmpty
alignment: provider.selectedProducts.isEmpty
? Alignment.center
: Alignment.bottomCenter,
child: Padding(
@ -100,73 +117,86 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height * 0.9,
),
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText:
'Search by name or SKU',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
return Consumer<ProductProvider>(
builder: (context, value, child) =>
StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(18.0),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText:
'Search by name or SKU',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
filterProducts(value);
setState(() {});
},
),
onChanged: (value) {
filterProducts(value);
setState(() {});
},
),
),
Expanded(
child: ListView.builder(
itemCount: filteredProducts.length,
itemBuilder: (context, index) {
bool isAlreadySelected =
selectedProducts.contains(
filteredProducts[index]);
return Card(
child: ListTile(
title: Text(
filteredProducts[index]
.name,
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors.black,
Expanded(
child: ListView.builder(
itemCount:
filteredProducts.length,
itemBuilder: (context, index) {
bool isAlreadySelected =
provider.selectedProducts
.contains(
filteredProducts[
index]);
return Card(
child: ListTile(
title: Text(
filteredProducts[index]
.name,
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors.black,
),
),
),
subtitle: Text(
filteredProducts[index].SKU,
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors.black,
subtitle: Text(
filteredProducts[index]
.SKU,
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors.black,
),
),
onTap: isAlreadySelected
? null
: () {
setState(() {
provider
.selectedProducts
.add(filteredProducts[
index]);
});
Navigator.pop(
context);
},
),
onTap: isAlreadySelected
? null
: () {
setState(() {
selectedProducts.add(
filteredProducts[
index]);
});
Navigator.pop(
context);
},
),
);
},
);
},
),
),
),
],
);
},
],
);
},
),
);
},
).whenComplete(() {
@ -180,7 +210,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
style: TextStyle(color: Colors.black),
),
),
if (selectedProducts.isNotEmpty) ...[
if (provider.selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0),
CommonElevatedButton(
borderRadius: 30,
@ -189,13 +219,21 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const DataSubmitSuccessfull(),
),
);
provider
.submitSelectedProducts(
"PrincipalDistributor",
widget.distributor.id!)
.then((value) {
if (value) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const DataSubmitSuccessfull(),
),
);
}
});
},
),
],
@ -213,9 +251,9 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
}
class ProductBlock extends StatefulWidget {
final Product product;
final int index;
const ProductBlock({super.key, required this.product});
const ProductBlock({super.key, required this.index});
@override
_ProductBlockState createState() => _ProductBlockState();
@ -225,27 +263,38 @@ class _ProductBlockState extends State<ProductBlock> {
final saleController = TextEditingController();
final inventoryController = TextEditingController();
String? errorMessage;
late ProductProvider provider;
@override
void initState() {
super.initState();
provider = Provider.of<ProductProvider>(context, listen: false);
}
void validateInput() {
bool validateInput() {
setState(() {
if (saleController.text.isNotEmpty &&
inventoryController.text.isNotEmpty) {
int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text);
if (inventory > sale) {
errorMessage = 'Inventory should be less than or equal to sales';
} else {
errorMessage = null;
// Check if both inputs are valid integers
try {
int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text);
// Validation: inventory should be less than or equal to sales
if (inventory > sale) {
errorMessage = 'Inventory should be less than or equal to sales';
} else {
errorMessage = null;
}
} catch (e) {
// Handle the case where input is not a valid integer
errorMessage = 'Please enter valid integer values for both fields';
}
} else {
errorMessage = null;
}
});
return errorMessage == null;
}
@override
@ -259,9 +308,9 @@ class _ProductBlockState extends State<ProductBlock> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Product: ${widget.product.name}',
Text('Product: ${provider.selectedProducts[widget.index].name}',
style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.SKU}',
Text('SKU: ${provider.selectedProducts[widget.index].SKU}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8),
TextField(
@ -270,7 +319,10 @@ class _ProductBlockState extends State<ProductBlock> {
keyboardType: TextInputType.number,
// enabled: widget.product.isPurchased,
enabled: true,
onChanged: (_) => validateInput(),
onChanged: (_) => validateInput()
? provider.selectedProducts[widget.index].sale =
int.parse(saleController.text)
: null,
),
TextField(
controller: inventoryController,
@ -278,7 +330,10 @@ class _ProductBlockState extends State<ProductBlock> {
keyboardType: TextInputType.number,
// enabled: widget.product.isPurchased,
enabled: true,
onChanged: (_) => validateInput(),
onChanged: (_) => validateInput()
? provider.selectedProducts[widget.index].inventory =
int.parse(inventoryController.text)
: null,
),
if (errorMessage != null)
Padding(

View File

@ -1,4 +1,5 @@
import 'package:cheminova/screens/select_sales_coordinator_screen.dart';
import 'package:cheminova/screens/assign_tasks_screen.dart';
import 'package:cheminova/screens/task_management_screen.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
@ -55,39 +56,40 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Assign Tasks',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child:
_customCard(title: "Total Tasks", subtitle: "100"),
),
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child: _customCard(
title: "Tasks Pending", subtitle: "100"),
),
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child: _customCard(
title: "Reports Submitted", subtitle: "100"),
),
],
),
// const Text(
// 'Assign Tasks',
// style: TextStyle(
// fontSize: 24,
// color: Colors.white,
// fontWeight: FontWeight.bold,
// fontFamily: 'Anek',
// ),
// ),
// const SizedBox(height: 10),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child:
// _customCard(title: "Total Tasks", subtitle: "100"),
// ),
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child: _customCard(
// title: "Tasks Pending", subtitle: "100"),
// ),
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child: _customCard(
// title: "Reports Submitted", subtitle: "100"),
// ),
// ],
// ),
const SizedBox(height: 20),
CommonElevatedButton(
backgroundColor: const Color(0xff004791),
borderRadius: 30,
@ -98,8 +100,7 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SelectSalesCoordinatorScreen(),
builder: (context) => const AssignTasksScreen(),
),
);
},
@ -111,7 +112,12 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
width: double.infinity,
height: kToolbarHeight - 10,
text: 'VIEW TASK STATUS',
onPressed: () {},
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TaskManagementScreen(),
),
),
),
const SizedBox(height: 15),
CommonElevatedButton(

View File

@ -1,9 +1,14 @@
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/screens/confirm_task_screen.dart';
import 'package: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';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:searchfield/searchfield.dart';
class AssignTasksScreen extends StatefulWidget {
const AssignTasksScreen({super.key});
@ -13,11 +18,65 @@ class AssignTasksScreen extends StatefulWidget {
}
class _AssignTasksScreenState extends State<AssignTasksScreen> {
final List<bool> _isChecked = List.generate(8, (_) => false);
bool _isSalesCoordinatorValid = true;
bool _isTaskValid = true;
bool _isPriorityValid = true;
bool _isNotesValid = true;
bool _isDistributorValid = true;
String? selectedDistributorType;
String _dateSelected = DateFormat('dd/MM/yyyy').format(DateTime.now());
void _validateAndSubmit() {
final taskProvider = context.read<TaskProvider>();
setState(() {
// Validate Sales Coordinator
_isSalesCoordinatorValid = taskProvider.selectedSalesCoordinator != null;
// Validate Task
_isTaskValid = taskProvider.selectedTask != null &&
taskProvider.selectedTask!.isNotEmpty;
// Validate Priority
_isPriorityValid = taskProvider.selectedPriority != null &&
taskProvider.selectedPriority!.isNotEmpty;
// Validate Notes
if ((taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD') &&
taskProvider.noteController.text.isEmpty) {
_isNotesValid = false;
} else {
_isNotesValid = true;
}
// Validate Distributor
if ((taskProvider.selectedTask != 'Collect KYC') &&
(taskProvider.selectedDistributor == null ||
selectedDistributorType == null)) {
_isDistributorValid = false;
} else {
_isDistributorValid = true;
}
});
// If all fields are valid, proceed to the next screen
if (_isSalesCoordinatorValid &&
_isTaskValid &&
_isPriorityValid &&
_isNotesValid &&
_isDistributorValid) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConfirmTaskScreen(
selectedDistributorType: selectedDistributorType,
),
),
);
}
}
Future<void> _selectDate(BuildContext context) async {
final provider = context.read<TaskProvider>();
final dateSelected = await showDatePicker(
context: context,
initialDate: DateTime.now(),
@ -25,14 +84,28 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
lastDate: DateTime(2025),
);
if (dateSelected != null) {
setState(() {
_dateSelected = DateFormat('dd/MM/yyyy').format(dateSelected);
});
provider.setDate(DateFormat('dd/MM/yyyy').format(dateSelected));
}
}
void _updateTaskProvider() {
final provider = context.read<TaskProvider>();
provider.clear();
provider.getSalesCoordinators();
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateTaskProvider();
});
}
@override
Widget build(BuildContext context) {
final taskProvider = context.watch<TaskProvider>();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
@ -56,250 +129,422 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: 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.5),
borderRadius: BorderRadius.circular(26.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Assign Tasks',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
child: Stack(
children: [
SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: 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.5),
borderRadius: BorderRadius.circular(26.0),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Sales Coordinator: Name",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Assign Tasks',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 20),
SearchField(
suggestionsDecoration: SuggestionDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8),
),
border: Border.all(
color: Colors.grey.withOpacity(0.5),
),
),
Text(
"ID: 121",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
suggestionItemDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.transparent,
style: BorderStyle.solid,
width: 1.0),
),
],
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Tasks List:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
searchInputDecoration: InputDecoration(
filled: true,
fillColor: Colors.grey.withOpacity(0.2),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.white,
width: 2.0,
),
borderRadius: BorderRadius.circular(8.0),
),
),
SizedBox(
height: 35,
child: CheckboxListTile(
value: _isChecked[0],
onChanged: (value) {
setState(() {
_isChecked[0] = value!;
});
},
title: const Text(
"Visit Retailers",
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(16.0),
),
borderSide: BorderSide(
color: Colors.white,
width: 2.0,
),
),
errorText: !_isSalesCoordinatorValid
? 'Please select a Sales Coordinator'
: null,
),
SizedBox(
height: 45,
child: CheckboxListTile(
value: _isChecked[1],
onChanged: (value) {
setState(() {
_isChecked[1] = value!;
});
},
title: const Text(
"Update Sales Data",
),
),
hint: 'Select Sales Coordinator',
onSuggestionTap: (selectedItem) {
if (selectedItem.item != null) {
taskProvider.setSelectedSalesCoordinator(
selectedItem.item!);
FocusScope.of(context).unfocus();
}
},
onTapOutside: (event) =>
FocusScope.of(context).unfocus(),
marginColor: Colors.grey.shade300,
suggestions: taskProvider.salesCoordinators
.map(
(e) => SearchFieldListItem(
e.name!,
item: e,
child: Text(e.name!),
),
)
.toList(),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
SizedBox(
height: 45,
child: CheckboxListTile(
value: _isChecked[2],
onChanged: (value) {
setState(() {
_isChecked[2] = value!;
});
},
title: const Text(
"Update Liqudation Data",
),
),
),
CheckboxListTile(
value: _isChecked[3],
onChanged: (value) {
setState(() {
_isChecked[3] = value!;
});
},
title: const Text(
"Collect KYC",
),
),
],
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Priority:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
value: _isChecked[4],
onChanged: (value) {
setState(() {
_isChecked[4] = value!;
});
},
),
const Text(
"Low",
"Tasks List:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Checkbox(
value: _isChecked[5],
SizedBox(
height: 35,
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Visit RD/PD',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
taskProvider.setSelectedTask(value);
},
title: const Text(
"Visit RD/PD",
),
),
),
SizedBox(
height: 45,
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Update Sales Data',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
taskProvider.setSelectedTask(value);
},
title: const Text(
"Update Sales Data Data",
),
),
),
SizedBox(
height: 45,
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Update Inventory Data',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
taskProvider.setSelectedTask(value);
},
title: const Text(
"Update Inventory Data",
),
),
),
RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Collect KYC',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
setState(() {
_isChecked[5] = value!;
});
taskProvider.setSelectedTask(value);
},
title: const Text(
"Collect KYC",
),
),
const Text(
"Medium",
if (!_isTaskValid &&
(taskProvider.selectedTask?.isEmpty ?? true))
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Please select a task',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
if (taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD') ...{
Container(
height: 150,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.0),
),
child: TextFormField(
controller: taskProvider.noteController,
expands: true,
maxLines: null,
minLines: null,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 14.0,
horizontal: 16.0,
),
border: InputBorder.none,
labelText: 'Note',
hintText: 'Enter your note',
errorText: !_isNotesValid
? 'Please enter a note'
: null,
),
Checkbox(
value: _isChecked[6],
),
),
const SizedBox(
height: 20,
)
},
if (taskProvider.selectedTask ==
'Update Inventory Data' ||
taskProvider.selectedTask == 'Visit RD/PD' ||
taskProvider.selectedTask == 'Update Sales Data') ...{
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 5,
),
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Select Distributor Type',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
),
value: selectedDistributorType,
items: [
'PrincipalDistributor',
'RetailDistributor'
].map((String type) {
return DropdownMenuItem<String>(
value: type,
child: Text(type),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedDistributorType = value;
selectedDistributorType ==
'PrincipalDistributor'
? taskProvider.getPd()
: taskProvider.getRd();
taskProvider.setSelectedDistributor(null);
});
},
),
),
// Dropdown for selecting distributor name based on type
if (selectedDistributorType != null)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<PdRdResponseModel>(
decoration: const InputDecoration(
labelText: 'Select Distributor Name',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
),
value: taskProvider.selectedDistributor,
items: (selectedDistributorType ==
'PrincipalDistributor'
? taskProvider.pdList
: taskProvider.rdList)
.map((PdRdResponseModel distributor) {
return DropdownMenuItem<PdRdResponseModel>(
value: distributor,
child: Text(distributor.name!),
);
}).toList(),
onChanged: (value) {
setState(() {
_isChecked[6] = value!;
});
taskProvider.setSelectedDistributor(value);
},
isExpanded: true,
isDense: true,
iconSize: 24,
hint: Text(
'Please select a ${selectedDistributorType ?? "Distributor Type"} first'),
),
),
if (!_isDistributorValid)
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Please select a distributor',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
},
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"High",
"Priority:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Row(
children: [
Radio<String>(
value: "Low",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
taskProvider.setSelectedPriority(value!);
},
),
const Text("Low"),
Radio<String>(
value: "Medium",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
taskProvider.setSelectedPriority(value!);
},
),
const Text("Medium"),
Radio<String>(
value: "High",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
taskProvider.setSelectedPriority(value!);
},
),
const Text("High"),
],
),
if (!_isPriorityValid &&
(taskProvider.selectedPriority?.isEmpty ??
true))
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Please select a priority',
style: TextStyle(color: Colors.red),
),
),
],
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Due Date:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Row(
children: [
Text(
taskProvider.selectedDate,
),
IconButton(
onPressed: () {
_selectDate(context);
},
icon: const Icon(Icons.calendar_month),
),
],
),
],
),
],
),
),
CommonElevatedButton(
backgroundColor: const Color(0xff004791),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONFIRM',
onPressed: _validateAndSubmit,
),
],
),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Due Date:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Row(
children: [
Text(
_dateSelected,
),
IconButton(
onPressed: () {
_selectDate(context);
},
icon: const Icon(Icons.calendar_month),
),
],
),
],
),
),
CommonElevatedButton(
backgroundColor: const Color(0xff004791),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONFIRM',
onPressed: () {},
),
],
),
),
),
),
),
if (taskProvider.isLoading)
Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(),
),
),
],
),
),
);

View File

@ -0,0 +1,239 @@
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ConfirmTaskScreen extends StatefulWidget {
final String? selectedDistributorType;
const ConfirmTaskScreen({
super.key,
this.selectedDistributorType,
});
@override
State<ConfirmTaskScreen> createState() => _ConfirmTaskScreenState();
}
class _ConfirmTaskScreenState extends State<ConfirmTaskScreen> {
@override
Widget build(BuildContext context) {
final taskProvider = context.watch<TaskProvider>();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: const Center(
child: Text(
'Confirm Task',
style: TextStyle(color: Colors.black87, fontSize: 20),
),
),
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: 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.5),
borderRadius: BorderRadius.circular(26.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Confirm Task',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 20),
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Sales Coordinator:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Text(
taskProvider.selectedSalesCoordinator!.name!,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
Text(
'ID: ${taskProvider.selectedSalesCoordinator!.uniqueId!}',
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
const Text(
'Tasks:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Task: ${taskProvider.selectedTask!}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Priority: ${taskProvider.selectedPriority!}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Deadline: ${taskProvider.selectedDate}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
if (taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD')
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Note:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Text(
taskProvider.noteController.text,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
if (taskProvider.selectedTask != 'Collect KYC') ...{
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.selectedDistributorType!,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Text(
taskProvider.selectedDistributor!.name!,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
],
)),
},
const SizedBox(height: 20),
CommonElevatedButton(
isLoading: taskProvider.isLoading,
backgroundColor: const Color(0xff004791),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONFIRM',
onPressed: () => taskProvider.assignTask(
context: context,
selectedDistributorType:
widget.selectedDistributorType != null
? widget.selectedDistributorType!
: '',
),
),
const SizedBox(height: 20),
CommonElevatedButton(
backgroundColor: const Color(0xff00784C),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'EDIT',
onPressed: () => Navigator.pop(context),
),
],
),
),
),
),
),
),
);
}
Widget _customContainer({required Widget child}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
// margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: child,
);
}
}

View File

@ -14,25 +14,32 @@ class DataSubmitSuccessfullState extends State<DataSubmitSuccessfull> {
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage()));
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => const HomePage()),
(Route<dynamic> route) => false, // Remove all routes
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
return Scaffold(
body: CommonBackground(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Data Submitted\nsuccessfully',
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', )]))));
const Text('Data Submitted\nsuccessfully',
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',
)
]))));
}
}

View File

@ -1,5 +1,6 @@
import 'package:cheminova/notification_service.dart';
import 'package:cheminova/provider/user_provider.dart';
import 'package:cheminova/screens/Visit_Dealers_screen.dart';
import 'package:cheminova/screens/assign_task_dash_board_screen.dart';
import 'package:cheminova/screens/calendar_screen.dart';
import 'package:cheminova/screens/collect_kyc_screen.dart';
@ -7,14 +8,12 @@ import 'package:cheminova/screens/mark_attendence_screen.dart';
import 'package:cheminova/screens/notification_screen.dart';
import 'package:cheminova/screens/products_manual_screen.dart';
import 'package:cheminova/screens/rejected_application_screen.dart';
import 'package:cheminova/screens/summary_screen.dart';
import 'package:cheminova/screens/product_sales_data.dart';
import 'package:cheminova/screens/update_inventory_screen.dart';
import 'package:cheminova/screens/display_sales_screen.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:flutter/material.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/screens/daily_tasks_screen.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
@ -106,17 +105,7 @@ class _HomePageState extends State<HomePage> {
const SizedBox(
height: 5,
),
_buildCustomCard('Daily Tasks', 'Dashboard', onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DailyTasksScreen(),
));
}),
const SizedBox(
height: 5,
),
_buildCustomCard('Assign Tasks', '', onTap: () {
_buildCustomCard('Assign Tasks', 'Dashboard', onTap: () {
Navigator.push(
context,
MaterialPageRoute(
@ -130,9 +119,8 @@ class _HomePageState extends State<HomePage> {
Row(
children: [
Expanded(
child: _buildCustomCard(
'Display\nSales data', 'Quickly display Sales',
onTap: () {
child: _buildCustomCard('Display\nSales data',
'Quickly display Sales\n', onTap: () {
Navigator.push(
context,
MaterialPageRoute(
@ -163,14 +151,19 @@ class _HomePageState extends State<HomePage> {
Row(
children: [
Expanded(
child:
_buildCustomCard('Summary', '\n\n', onTap: () {
Navigator.push(
child: _buildCustomCard(
'Visit RD/PD',
'\n\n',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SummaryScreen(),
));
}),
builder: (context) =>
const VisitDealersScreen(),
),
);
},
),
),
const SizedBox(
width: 12,

View File

@ -19,7 +19,7 @@ class ProductSalesDataState extends State<ProductSalesData> {
text: DateFormat('dd/MM/yyyy').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();
@ -29,19 +29,19 @@ class ProductSalesDataState extends State<ProductSalesData> {
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: ()
{
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'), padding: const EdgeInsets.only(right: 20),
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: const Text('Product Sales Data',
@ -49,7 +49,9 @@ class ProductSalesDataState extends State<ProductSalesData> {
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(
@ -63,7 +65,7 @@ class ProductSalesDataState extends State<ProductSalesData> {
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),
// margin: const EdgeInsets.symmetric(horizontal: 30.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
@ -77,42 +79,42 @@ class ProductSalesDataState extends State<ProductSalesData> {
fillColor: Colors.white,
controller: dealercontroller),
const SizedBox(height: 15),
CommonTextFormField(
title: 'Select Product: Product A',
fillColor: Colors.white,
controller: productController),
const SizedBox(height: 15),
CommonTextFormField(
title: 'Date Range: 10-06 to 20-06',
readOnly: true,
fillColor: Colors.white,
controller: dateController),
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),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const DataSubmitSuccessfull(),));
},
)
),
],
alignment: Alignment.center,
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 DataSubmitSuccessfull(),
));
},
)),
],
),
),
],
),
),
),),
),
),
);
}
}
}

View File

@ -1,4 +1,8 @@
import 'package:cheminova/models/product_manual_model.dart';
import 'package:cheminova/provider/product_manual_provider.dart';
import 'package:cheminova/screens/view_pdf_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
@ -9,6 +13,13 @@ class ProductsManualScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Access the provider
final productManualProvider =
Provider.of<ProductManualProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
productManualProvider.getProductManualList();
});
return CommonBackground(
child: Scaffold(
backgroundColor: Colors.transparent,
@ -22,12 +33,14 @@ class ProductsManualScreen extends StatelessWidget {
padding: const EdgeInsets.only(right: 20),
),
],
title: const Text('Products Manual',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
title: const Text(
'Products Manual',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek'),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
@ -41,25 +54,39 @@ class ProductsManualScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const SizedBox(height: 16),
Container(
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: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildProductButton('Product 1'),
_buildProductButton('Product 2'),
_buildProductButton('Product 3'),
_buildProductButton('Product 4'),
_buildProductButton('Product 5'),
_buildProductButton('Product 6'),
_buildProductButton('Product 7'),
],
),
Consumer<ProductManualProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (provider.productManualList.isEmpty) {
return const Center(
child: Text('No products available.'),
);
}
return Container(
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: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: provider.productManualList
.map(
(product) =>
_buildProductButton(context, product),
)
.toList(),
),
);
},
),
],
),
@ -69,20 +96,22 @@ class ProductsManualScreen extends StatelessWidget {
);
}
Widget _buildProductButton(String productName) {
Widget _buildProductButton(BuildContext context, ProductManualModel product) {
return Padding(
padding: const EdgeInsets.only(bottom: 15),
child: CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: productName,
text: product.title,
backgroundColor: const Color(0xff004791),
onPressed: () {
// Handle product button press
debugPrint('$productName pressed');
},
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ViewPdfScreen(productManualModel: product),
),
),
),
);
}
}
}

View File

@ -0,0 +1,103 @@
import 'package:cheminova/provider/user_provider.dart';
import 'package:flutter/material.dart';
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';
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
CommonBackground(
isFullWidth: true,
child: Scaffold(
drawer: const CommonDrawer(),
backgroundColor: Colors.transparent,
appBar: CommonAppBar(
title: const Text('Profile'),
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
),
body: Consumer<UserProvider>(
builder: (context, userProvider, child) {
final profileData = userProvider.user;
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: [
const 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 ?? ''),
],
),
),
],
),
);
},
),
),
),
],
);
}
Widget _buildProfileItem(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xff004791),
),
),
const SizedBox(height: 5),
Text(
value,
style: const TextStyle(
fontSize: 18,
color: Colors.black,
),
),
const Divider(color: Colors.grey),
],
),
);
}
}

View File

@ -1,4 +1,3 @@
import 'package:cheminova/screens/assign_tasks_screen.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
@ -79,10 +78,18 @@ class _SelectSalesCoordinatorScreenState
child: ListView.builder(
itemCount: salesCoordinators.length,
itemBuilder: (context, index) {
return _customCard(
name: salesCoordinators[index].name,
id: salesCoordinators[index].id,
tasks: salesCoordinators[index].tasks,
return GestureDetector(
onTap: () {
// taskProvider.setSelectedSalesCoordinator(
// salesCoordinators[index],
// );
// Navigator.pop(context);
},
child: _customCard(
name: salesCoordinators[index].name,
id: salesCoordinators[index].id,
tasks: salesCoordinators[index].tasks,
),
);
},
),
@ -98,55 +105,43 @@ class _SelectSalesCoordinatorScreenState
Widget _customCard(
{required String name, required String id, required int tasks}) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const AssignTasksScreen();
},
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
),
);
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
const SizedBox(height: 5),
Text(
"ID: $id",
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
const SizedBox(height: 5),
Text(
"ID: $id",
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
),
const SizedBox(height: 5),
Text(
"Tasks: $tasks",
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
const SizedBox(height: 5),
Text(
"Tasks: $tasks",
style: const TextStyle(
fontFamily: 'Anek',
fontSize: 16,
),
),
],
),
),
],
),
);
}

View File

@ -0,0 +1,272 @@
import 'package:cheminova/models/task_model.dart';
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_text_form_field.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class TaskManagementScreen extends StatefulWidget {
const TaskManagementScreen({super.key});
@override
State<TaskManagementScreen> createState() => _TaskManagementScreenState();
}
class _TaskManagementScreenState extends State<TaskManagementScreen> {
final TextEditingController _searchController = TextEditingController();
List<TaskModel> _filteredTaskList = [];
String? _selectedChip = 'New'; // State to track the selected chip
void _filterTaskList(String query) {
if (query.isEmpty || query == '' || query == ' ') {
setState(() {
_filteredTaskList = context.read<TaskProvider>().taskModelList;
});
return;
}
setState(() {
_filteredTaskList = _filteredTaskList
.where((task) => task.taskAssignedTo.name
.toLowerCase()
.contains(query.toLowerCase()))
.toList();
});
}
Future<void> _updateTaskProvider() async {
final provider = context.read<TaskProvider>();
provider.clear();
await provider.getNewTasks();
_filteredTaskList.clear();
_filteredTaskList = provider.taskModelList;
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateTaskProvider();
});
}
@override
Widget build(BuildContext context) {
final taskProvider = context.watch<TaskProvider>();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: const Center(
child: Text(
'Task Management',
style: TextStyle(color: Colors.black87, fontSize: 20),
),
),
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: Stack(
children: [
SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: 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.5),
borderRadius: BorderRadius.circular(26.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CommonTextFormField(
controller: _searchController,
onChanged: (value) => _filterTaskList(value),
title: 'Filter by Sales Coordinator:',
),
const SizedBox(height: 20),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_filterChip(
title: 'New',
icon: const Icon(Icons.new_releases),
onSelected: () async {
setState(() {
_selectedChip = 'New';
});
await taskProvider.getNewTasks();
_filteredTaskList.clear();
_filteredTaskList =
taskProvider.taskModelList;
},
isSelected: _selectedChip == 'New',
),
const SizedBox(width: 5),
_filterChip(
title: 'Pending',
icon: const Icon(Icons.pending),
onSelected: () async {
setState(() {
_selectedChip = 'Pending';
});
await taskProvider.getPendingTasks();
_filteredTaskList.clear();
_filteredTaskList =
taskProvider.taskModelList;
},
isSelected: _selectedChip == 'Pending',
),
const SizedBox(width: 5),
_filterChip(
title: 'Completed',
icon: const Icon(Icons.done),
onSelected: () async {
setState(() {
_selectedChip = 'Completed';
});
await taskProvider.getCompletedTasks();
_filteredTaskList.clear();
_filteredTaskList =
taskProvider.taskModelList;
},
isSelected: _selectedChip == 'Completed',
),
],
),
),
const SizedBox(height: 10),
SizedBox(
height: MediaQuery.of(context).size.height * 0.55,
child: _filteredTaskList.isEmpty
? const Center(child: Text('No tasks found'))
: ListView.builder(
itemCount: _filteredTaskList.length,
itemBuilder: (context, index) => _taskView(
task: _filteredTaskList[
_selectedChip == 'Completed'
? index
: ((_filteredTaskList.length - 1) -
index)],
),
),
),
],
),
),
),
),
),
if (taskProvider.isLoading)
Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(),
),
),
],
),
),
);
}
Widget _customContainer({required Widget child}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: child,
);
}
Widget _taskView({required TaskModel task}) {
final formatedDate = DateFormat('dd/MM/yyyy').format(task.taskDueDate);
return Column(
children: [
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Assigned to: ${task.taskAssignedTo.name}",
style: const TextStyle(
fontFamily: 'Anek',
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"Task: ${task.task}",
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Status: ${task.taskStatus}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Deadline:$formatedDate',
style: const TextStyle(
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 10),
],
);
}
Widget _filterChip({
required String title,
required Widget icon,
required VoidCallback onSelected,
required bool isSelected,
}) {
return GestureDetector(
onTap: onSelected,
child: Chip(
avatar: icon,
label: Text(
title,
style: const TextStyle(
fontFamily: 'Anek',
),
),
backgroundColor: isSelected ? Colors.blue : Colors.grey[200],
),
);
}
}

View File

@ -1,9 +1,12 @@
import 'package:cheminova/screens/Add_products_screen.dart';
import 'package:flutter/material.dart';
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/screens/add_products_screen.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UpdateInventoryScreen extends StatefulWidget {
const UpdateInventoryScreen({super.key});
@ -13,14 +16,17 @@ class UpdateInventoryScreen extends StatefulWidget {
}
class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
final List<String> principalDistributors = ['vaibhav', 'sonu', 'monu'];
final List<String> retailerDistributors = ['shivam', 'vivek'];
String? selectedDistributorType;
String? selectedDistributor;
PdRdResponseModel? selectedDistributor;
@override
void initState() {
super.initState();
// Fetch the PdRd data when the screen is initialized
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final provider = Provider.of<PdRdProvider>(context, listen: false);
// provider.getPdRd();
// });
}
@override
@ -38,12 +44,14 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
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: const Text(
'Update Inventory Data',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek'),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
@ -57,84 +65,111 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddProductsScreen(),
),
);
if (selectedDistributor == null ||
selectedDistributorType == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Please select distributor type and distributor')),
);
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddProductsScreen(
distributor: selectedDistributor!,
distributorType: selectedDistributorType!,
),
),
);
}
},
),
),
body: Stack(
children: [
Column(
body: Consumer<PdRdProvider>(
builder: (context, provider, child) {
return Stack(
children: [
// Dropdown for selecting distributor type
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Select Distributor Type',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
Column(
children: [
// Dropdown for selecting distributor type
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Select Distributor Type',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
),
value: selectedDistributorType,
items: ['Principal Distributor', 'Retailer Distributor']
.map((String type) {
return DropdownMenuItem<String>(
value: type,
child: Text(type),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedDistributorType = value;
selectedDistributorType == 'Principal Distributor'
? provider.getPd()
: provider.getRd();
selectedDistributor =
null; // Reset distributor selection when type changes
});
},
),
),
value: selectedDistributorType,
items: ['Principal Distributor', 'Retailer Distributor']
.map((String type) {
return DropdownMenuItem<String>(
value: type,
child: Text(type),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedDistributorType = value;
selectedDistributor =
null; // Reset distributor selection when type changes
});
},
),
// Dropdown for selecting distributor name based on type
if (selectedDistributorType != null)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<PdRdResponseModel>(
decoration: const InputDecoration(
labelText: 'Select Distributor Name',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
),
value: selectedDistributor,
items: (selectedDistributorType ==
'Principal Distributor'
? provider.pdList
: provider.rdList)
.map((PdRdResponseModel distributor) {
return DropdownMenuItem<PdRdResponseModel>(
value: distributor,
child: Text(distributor.name!),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedDistributor = value;
});
},
isExpanded: true,
isDense: true,
iconSize: 24,
hint: Text(
'Please select a ${selectedDistributorType ?? "Distributor Type"} first'),
),
),
],
),
// Dropdown for selecting distributor name based on type
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25),
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Select Distributor Name',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(),
if (provider.isLoading)
Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(),
),
value: selectedDistributor,
items: (selectedDistributorType == 'Principal Distributor'
? principalDistributors
: retailerDistributors)
.map((String distributor) {
return DropdownMenuItem<String>(
value: distributor,
child: Text(distributor),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedDistributor = value;
});
},
// Disable the dropdown if no distributor type is selected
isExpanded: true,
isDense: true,
iconSize: 24,
hint: Text(
'Please select a ${selectedDistributorType ?? "Distributor Type"} first'),
),
),
],
),
],
);
},
),
),
);

View File

@ -0,0 +1,24 @@
import 'package:cheminova/models/product_manual_model.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
class ViewPdfScreen extends StatefulWidget {
final ProductManualModel productManualModel;
const ViewPdfScreen({super.key, required this.productManualModel});
@override
State<ViewPdfScreen> createState() => _ViewPdfScreenState();
}
class _ViewPdfScreenState extends State<ViewPdfScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SfPdfViewer.network(
widget.productManualModel.productManualDetail.url,
),
),
);
}
}

View File

@ -12,4 +12,12 @@ class ApiUrls {
static const String notificationUrl = '$baseUrl/get-notification-tm';
static const String fcmUrl = '${baseUrl}kyc/save-fcm-tm';
static const String getProducts = '${baseUrl}product/getAll/user/';
static const String getRd = 'inventory/distributors-TM/RetailDistributor';
static const String getPd = 'inventory/distributors-TM/PrincipalDistributor';
static const String submitProducts = 'inventory/add-TM';
static const String getSalesCoordinators = 'salescoordinator/getAll-TM';
static const String assignTask = 'task/assign-task';
static const String getProductsManual = 'productmanual/getall';
static const String getAllTasks = 'task/alltasks/';
static const String updateTaskInventoryUrl = '';
}

View File

@ -2,6 +2,7 @@ import 'package:cheminova/provider/user_provider.dart';
import 'package:cheminova/screens/change_password_screen.dart';
import 'package:cheminova/screens/home_screen.dart';
import 'package:cheminova/screens/login_screen.dart';
import 'package:cheminova/screens/profile_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -72,7 +73,14 @@ class CommonDrawer extends StatelessWidget {
leading: const Icon(Icons.account_circle),
title: const Text('Profile'),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const ProfileScreen();
},
),
);
},
),
ListTile(

View File

@ -13,6 +13,7 @@ class CommonTextFormField extends StatelessWidget {
final List<TextInputFormatter>? inputFormatters;
final int? maxLength;
final bool obscureText;
final void Function(String)? onChanged;
const CommonTextFormField({
super.key,
@ -26,6 +27,7 @@ class CommonTextFormField extends StatelessWidget {
this.keyboardType,
this.inputFormatters,
this.maxLength,
this.onChanged,
this.obscureText = false,
});
@ -45,6 +47,7 @@ class CommonTextFormField extends StatelessWidget {
readOnly: readOnly ?? false,
maxLines: maxLines,
maxLength: maxLength,
onChanged: onChanged,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
validator: validator,
keyboardType: keyboardType,

View File

@ -8,6 +8,7 @@
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
@ -16,4 +17,7 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
flutter_secure_storage_linux
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import device_info_plus
import file_selector_macos
import firebase_analytics
import firebase_core
@ -14,8 +15,11 @@ import flutter_local_notifications
import flutter_secure_storage_macos
import geolocator_apple
import path_provider_foundation
import syncfusion_pdfviewer_macos
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
@ -25,4 +29,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@ -225,6 +225,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
device_info_plus:
dependency: transitive
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
url: "https://pub.dev"
source: hosted
version: "10.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
dio:
dependency: "direct main"
description:
@ -1040,6 +1056,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
searchfield:
dependency: "direct main"
description:
name: searchfield
sha256: "913bb61f42d47d82c1adb67047fff3b26dcf6a199f71ecdc9af27c506dda4b48"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
shelf:
dependency: transitive
description:
@ -1117,6 +1141,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: "85f96b7b06f32a60b19ab0bb8c32bf162a0474f2d94f4844384be1118e6b4954"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_flutter_pdf:
dependency: transitive
description:
name: syncfusion_flutter_pdf
sha256: "0bf5986a8ea7afc54d5712c54f1aba8530be0920872c9dbbe5e0c4369d51eef8"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_flutter_pdfviewer:
dependency: "direct main"
description:
name: syncfusion_flutter_pdfviewer
sha256: "25ca744376c5dafe2e3e5c7718ccb7377a21ddbba00c0d6524801c6ed634c493"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_flutter_signaturepad:
dependency: transitive
description:
name: syncfusion_flutter_signaturepad
sha256: "54545a6611ec8f4b975269f5fd1e7c996172d41398745363542d1307d2976884"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_pdfviewer_macos:
dependency: transitive
description:
name: syncfusion_pdfviewer_macos
sha256: "5731a1ea92455b8322a647387f27c2e7f52b963dff3983b8207233ecc6ef7ba7"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_pdfviewer_platform_interface:
dependency: transitive
description:
name: syncfusion_pdfviewer_platform_interface
sha256: f266aa4657b511201666f394b74c71ecfeaafa2717e4e75e73585a55d99d350d
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_pdfviewer_web:
dependency: transitive
description:
name: syncfusion_pdfviewer_web
sha256: "7a3da6586668b851b74ab1bda09d313a54250fb0f74daaadda7133ca3591b235"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
syncfusion_pdfviewer_windows:
dependency: transitive
description:
name: syncfusion_pdfviewer_windows
sha256: "670ac5e4158ba5060fbb26e2a5a04f6d7272eb9f4948d470634a0dbf33e423b2"
url: "https://pub.dev"
source: hosted
version: "26.2.11"
table_calendar:
dependency: "direct main"
description:
@ -1181,6 +1269,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
url_launcher:
dependency: transitive
description:
name: url_launcher
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
url: "https://pub.dev"
source: hosted
version: "6.3.9"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
url: "https://pub.dev"
source: hosted
version: "6.3.1"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
url: "https://pub.dev"
source: hosted
version: "3.2.0"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
uuid:
dependency: transitive
description:
@ -1269,6 +1421,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.5.3"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
url: "https://pub.dev"
source: hosted
version: "1.1.4"
xdg_directories:
dependency: transitive
description:

View File

@ -50,6 +50,8 @@ dependencies:
flutter_local_notifications: ^17.2.1+2
firebase_crashlytics: ^4.0.4
firebase_analytics: ^11.2.1
searchfield: ^1.0.9
syncfusion_flutter_pdfviewer: ^26.2.11
dev_dependencies:
flutter_test:

View File

@ -11,6 +11,8 @@
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <syncfusion_pdfviewer_windows/syncfusion_pdfviewer_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
@ -23,4 +25,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -8,6 +8,8 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
geolocator_windows
permission_handler_windows
syncfusion_pdfviewer_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST