Assign task and Product manual

This commit is contained in:
kratikpal 2024-08-29 10:30:47 +05:30
parent 1436aa0131
commit 12af640bae
18 changed files with 663 additions and 214 deletions

View File

@ -2,6 +2,7 @@ import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:cheminova/provider/collect_kyc_provider.dart'; import 'package:cheminova/provider/collect_kyc_provider.dart';
import 'package:cheminova/provider/pd_rd_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/product_provider.dart';
import 'package:cheminova/provider/task_provider.dart'; import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/provider/user_provider.dart'; import 'package:cheminova/provider/user_provider.dart';
@ -22,8 +23,10 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
} }
AndroidNotificationChannel channel = const AndroidNotificationChannel( AndroidNotificationChannel channel = const AndroidNotificationChannel(
'High Importance Channel', 'High Importance Notifications', 'High Importance Channel',
importance: Importance.high); 'High Importance Notifications',
importance: Importance.high,
);
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
@ -102,6 +105,7 @@ Future<void> main() async {
ChangeNotifierProvider(create: (_) => ProductProvider()), ChangeNotifierProvider(create: (_) => ProductProvider()),
ChangeNotifierProvider(create: (_) => PdRdProvider()), ChangeNotifierProvider(create: (_) => PdRdProvider()),
ChangeNotifierProvider(create: (_) => TaskProvider()), ChangeNotifierProvider(create: (_) => TaskProvider()),
ChangeNotifierProvider(create: (_) => ProductManualProvider()),
], ],
child: const MyApp(), child: const MyApp(),
), ),

View File

@ -2,57 +2,17 @@ class PdRdResponseModel {
String? id; String? id;
String? uniqueId; String? uniqueId;
String? name; String? name;
String? tradeName; String? tradeNameRd;
String? address; ShippingAddress? shippingAddress;
String? state; String? salesCoordinator; // Nullable property for SalesCoordinator
String? city;
String? district;
String? pincode;
String? mobileNumber;
String? principalDistributer;
String? panNumber;
ImageModel? panImg;
String? aadharNumber;
ImageModel? aadharImg;
String? gstNumber;
ImageModel? gstImg;
ImageModel? pesticideLicenseImg;
ImageModel? selfieEntranceImg;
String? status;
String? addedBy;
String? userType;
List<Note>? notes;
DateTime? createdAt;
DateTime? updatedAt;
int? v;
PdRdResponseModel({ PdRdResponseModel({
this.id, this.id,
this.uniqueId, this.uniqueId,
this.name, this.name,
this.tradeName, this.tradeNameRd,
this.address, this.shippingAddress,
this.state, this.salesCoordinator, // Initialize SalesCoordinator
this.city,
this.district,
this.pincode,
this.mobileNumber,
this.principalDistributer,
this.panNumber,
this.panImg,
this.aadharNumber,
this.aadharImg,
this.gstNumber,
this.gstImg,
this.pesticideLicenseImg,
this.selfieEntranceImg,
this.status,
this.addedBy,
this.userType,
this.notes,
this.createdAt,
this.updatedAt,
this.v,
}); });
factory PdRdResponseModel.fromJson(Map<String, dynamic> json) => factory PdRdResponseModel.fromJson(Map<String, dynamic> json) =>
@ -60,101 +20,53 @@ class PdRdResponseModel {
id: json["_id"], id: json["_id"],
name: json["name"], name: json["name"],
uniqueId: json["uniqueId"], uniqueId: json["uniqueId"],
// tradeName: json["trade_name"], tradeNameRd: json["trade_name"],
// address: json["address"], shippingAddress: json['shippingAddress'] != null
// state: json["state"], ? ShippingAddress.fromJson(json['shippingAddress'])
// city: json["city"], : null,
// district: json["district"], salesCoordinator: json.containsKey('salesCoordinator')
// pincode: json["pincode"], ? json['salesCoordinator']
// mobileNumber: json["mobile_number"], : null, // Handle missing field
// principalDistributer: json["principal_distributer"],
// panNumber: json["pan_number"],
// panImg: ImageModel.fromJson(json["pan_img"]),
// aadharNumber: json["aadhar_number"],
// aadharImg: ImageModel.fromJson(json["aadhar_img"]),
// gstNumber: json["gst_number"],
// gstImg: ImageModel.fromJson(json["gst_img"]),
// pesticideLicenseImg: ImageModel.fromJson(json["pesticide_license_img"]),
// selfieEntranceImg: ImageModel.fromJson(json["selfie_entrance_img"]),
// status: json["status"],
// addedBy: json["addedBy"],
// userType: json["userType"],
// notes: List<Note>.from(json["notes"].map((x) => Note.fromJson(x))),
// createdAt: DateTime.parse(json["createdAt"]),
// updatedAt: DateTime.parse(json["updatedAt"]),
// v: json["__v"],
); );
Map<String, dynamic> toJson() => {
"_id": id,
"name": name,
"uniqueId": uniqueId,
// "trade_name": tradeName,
// "address": address,
// "state": state,
// "city": city,
// "district": district,
// "pincode": pincode,
// "mobile_number": mobileNumber,
// "principal_distributer": principalDistributer,
// "pan_number": panNumber,
// "pan_img": panImg.toJson(),
// "aadhar_number": aadharNumber,
// "aadhar_img": aadharImg.toJson(),
// "gst_number": gstNumber,
// "gst_img": gstImg.toJson(),
// "pesticide_license_img": pesticideLicenseImg.toJson(),
// "selfie_entrance_img": selfieEntranceImg.toJson(),
// "status": status,
// "addedBy": addedBy,
// "userType": userType,
// "notes": List<dynamic>.from(notes.map((x) => x.toJson())),
// "createdAt": createdAt.toIso8601String(),
// "updatedAt": updatedAt.toIso8601String(),
// "__v": v,
};
} }
class ImageModel { class ShippingAddress {
String? publicId; final String id;
String? url; 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;
ImageModel({ ShippingAddress({
this.publicId, required this.id,
this.url, 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 ImageModel.fromJson(Map<String, dynamic> json) => ImageModel( factory ShippingAddress.fromJson(Map<String, dynamic> json) {
publicId: json["public_id"], return ShippingAddress(
url: json["url"], id: json['_id'] ?? '',
); street: json['street'] ?? '',
city: json['city'] ?? '',
Map<String, dynamic> toJson() => { state: json['state'] ?? '',
"public_id": publicId, postalCode: json['postalCode'] ?? '',
"url": url, country: json['country'] ?? '',
}; panNumber: json['panNumber'] ?? '',
} tradeName: json['tradeName'] ?? '',
gstNumber: json['gstNumber'] ?? '',
class Note { isDefault: json['isDefault'] ?? false,
String? message; );
DateTime? replyDate; }
String? id;
Note({
this.message,
this.replyDate,
this.id,
});
factory Note.fromJson(Map<String, dynamic> json) => Note(
message: json["message"],
replyDate: DateTime.parse(json["replyDate"]),
id: json["_id"],
);
Map<String, dynamic> toJson() => {
"message": message,
// "replyDate": replyDate.toIso8601String(),
"_id": id,
};
} }

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

@ -37,10 +37,11 @@ class Product {
final String SKU; final String SKU;
final String name; final String name;
final Category category; final Category category;
final Brand brand;
final double price; final double price;
final GST gst; final int GST;
final int HSNCode;
final String description; final String description;
final String specialInstructions;
final String productStatus; final String productStatus;
final AddedBy addedBy; final AddedBy addedBy;
final List<dynamic> image; final List<dynamic> image;
@ -55,10 +56,11 @@ class Product {
required this.SKU, required this.SKU,
required this.name, required this.name,
required this.category, required this.category,
required this.brand,
required this.price, required this.price,
required this.gst, required this.GST,
required this.HSNCode,
required this.description, required this.description,
required this.specialInstructions,
required this.productStatus, required this.productStatus,
required this.addedBy, required this.addedBy,
required this.image, required this.image,
@ -75,10 +77,11 @@ class Product {
SKU: json['SKU'], SKU: json['SKU'],
name: json['name'], name: json['name'],
category: Category.fromJson(json['category']), category: Category.fromJson(json['category']),
brand: Brand.fromJson(json['brand']),
price: (json['price'] as num).toDouble(), price: (json['price'] as num).toDouble(),
gst: GST.fromJson(json['GST']), GST: json['GST'],
description: json['description'], HSNCode: json['HSN_Code'],
specialInstructions: json['special_instructions'], description: json['description'] ?? '',
productStatus: json['product_Status'], productStatus: json['product_Status'],
addedBy: AddedBy.fromJson(json['addedBy']), addedBy: AddedBy.fromJson(json['addedBy']),
image: json['image'] as List<dynamic>, image: json['image'] as List<dynamic>,
@ -96,10 +99,11 @@ class Product {
'SKU': SKU, 'SKU': SKU,
'name': name, 'name': name,
'category': category.toJson(), 'category': category.toJson(),
'brand': brand.toJson(),
'price': price, 'price': price,
'GST': gst.toJson(), 'GST': GST,
'HSN_Code': HSNCode,
'description': description, 'description': description,
'special_instructions': specialInstructions,
'product_Status': productStatus, 'product_Status': productStatus,
'addedBy': addedBy.toJson(), 'addedBy': addedBy.toJson(),
'image': image, 'image': image,
@ -136,30 +140,26 @@ class Category {
} }
} }
class GST { class Brand {
final String id; final String id;
final String name; final String brandName;
final int tax;
GST({ Brand({
required this.id, required this.id,
required this.name, required this.brandName,
required this.tax,
}); });
factory GST.fromJson(Map<String, dynamic> json) { factory Brand.fromJson(Map<String, dynamic> json) {
return GST( return Brand(
id: json['_id'], id: json['_id'],
name: json['name'], brandName: json['brandName'],
tax: json['tax'],
); );
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'_id': id, '_id': id,
'name': name, 'brandName': brandName,
'tax': tax,
}; };
} }
} }

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

@ -13,6 +13,9 @@ class TaskProvider extends ChangeNotifier {
String? _selectedPriority; String? _selectedPriority;
String _selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now()); String _selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
List<PdRdResponseModel> _salesCoordinators = []; List<PdRdResponseModel> _salesCoordinators = [];
List<PdRdResponseModel> _pdList = [];
List<PdRdResponseModel> _rdList = [];
PdRdResponseModel? _selectedDistributor;
final TextEditingController _noteController = TextEditingController(); final TextEditingController _noteController = TextEditingController();
final _apiClient = ApiClient(); final _apiClient = ApiClient();
@ -23,6 +26,9 @@ class TaskProvider extends ChangeNotifier {
String get selectedDate => _selectedDate; String get selectedDate => _selectedDate;
List<PdRdResponseModel> get salesCoordinators => _salesCoordinators; List<PdRdResponseModel> get salesCoordinators => _salesCoordinators;
TextEditingController get noteController => _noteController; TextEditingController get noteController => _noteController;
List<PdRdResponseModel> get pdList => _pdList;
List<PdRdResponseModel> get rdList => _rdList;
PdRdResponseModel? get selectedDistributor => _selectedDistributor;
void setLoading(bool loading) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
@ -49,7 +55,7 @@ class TaskProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void clear(){ void clear() {
_selectedSalesCoordinator = null; _selectedSalesCoordinator = null;
_selectedTask = null; _selectedTask = null;
_selectedPriority = null; _selectedPriority = null;
@ -79,19 +85,39 @@ class TaskProvider extends ChangeNotifier {
} }
} }
Future<void> assignTask(BuildContext context) async { Future<void> assignTask({
required BuildContext context,
String? selectedDistributorType,
}) async {
setLoading(true); setLoading(true);
print("addedFff:$selectedDistributorType");
try { 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( Response response = await _apiClient.post(
ApiUrls.assignTask, ApiUrls.assignTask,
data: { data: data,
'taskAssignedTo': _selectedSalesCoordinator!.id,
'task': _selectedTask,
'taskPriority': _selectedPriority,
'taskDueDate': _selectedDate,
'note': _noteController.text,
},
); );
if (response.statusCode == 201) { if (response.statusCode == 201) {
Navigator.push( Navigator.push(
context, context,
@ -108,4 +134,57 @@ class TaskProvider extends ChangeNotifier {
setLoading(false); 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);
}
}
} }

View File

@ -1,3 +1,4 @@
import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/provider/task_provider.dart'; import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/screens/confirm_task_screen.dart'; import 'package:cheminova/screens/confirm_task_screen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -21,6 +22,8 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
bool _isTaskValid = true; bool _isTaskValid = true;
bool _isPriorityValid = true; bool _isPriorityValid = true;
bool _isNotesValid = true; bool _isNotesValid = true;
bool _isDistributorValid = true;
String? selectedDistributorType;
void _validateAndSubmit() { void _validateAndSubmit() {
final taskProvider = context.read<TaskProvider>(); final taskProvider = context.read<TaskProvider>();
@ -37,18 +40,37 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
taskProvider.selectedPriority!.isNotEmpty; taskProvider.selectedPriority!.isNotEmpty;
// Validate Notes // Validate Notes
if (taskProvider.selectedTask == 'Collect KYC' && if ((taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD') &&
taskProvider.noteController.text.isEmpty) { taskProvider.noteController.text.isEmpty) {
_isNotesValid = false; _isNotesValid = false;
} else {
_isNotesValid = true;
}
// Validate Distributor
if ((taskProvider.selectedTask == 'Update Inventory Data' ||
taskProvider.selectedTask == 'Visit RD/PD') &&
(taskProvider.selectedDistributor == null ||
selectedDistributorType == null)) {
_isDistributorValid = false;
} else {
_isDistributorValid = true;
} }
}); });
// If all fields are valid, proceed to the next screen // If all fields are valid, proceed to the next screen
if (_isSalesCoordinatorValid && _isTaskValid && _isPriorityValid) { if (_isSalesCoordinatorValid &&
_isTaskValid &&
_isPriorityValid &&
_isNotesValid &&
_isDistributorValid) {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const ConfirmTaskScreen(), builder: (context) => ConfirmTaskScreen(
selectedDistributorType: selectedDistributorType,
),
), ),
); );
} }
@ -225,13 +247,13 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
height: 35, height: 35,
child: RadioListTile<String>( child: RadioListTile<String>(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
value: 'Visit Retailers', value: 'Visit RD/PD',
groupValue: taskProvider.selectedTask, groupValue: taskProvider.selectedTask,
onChanged: (value) { onChanged: (value) {
taskProvider.setSelectedTask(value); taskProvider.setSelectedTask(value);
}, },
title: const Text( title: const Text(
"Visit Retailers", "Visit RD/PD",
), ),
), ),
), ),
@ -253,13 +275,13 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
height: 45, height: 45,
child: RadioListTile<String>( child: RadioListTile<String>(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
value: 'Update Liqudation Data', value: 'Update Inventory Data',
groupValue: taskProvider.selectedTask, groupValue: taskProvider.selectedTask,
onChanged: (value) { onChanged: (value) {
taskProvider.setSelectedTask(value); taskProvider.setSelectedTask(value);
}, },
title: const Text( title: const Text(
"Update Liqudation Data", "Update Inventory Data",
), ),
), ),
), ),
@ -290,7 +312,8 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
], ],
), ),
), ),
if (taskProvider.selectedTask == 'Collect KYC') ...{ if (taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD') ...{
Container( Container(
height: 150, height: 150,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -320,6 +343,88 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
height: 20, height: 20,
) )
}, },
if (taskProvider.selectedTask ==
'Update Inventory Data' ||
taskProvider.selectedTask == 'Visit RD/PD') ...{
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) {
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( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),

View File

@ -7,7 +7,11 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class ConfirmTaskScreen extends StatefulWidget { class ConfirmTaskScreen extends StatefulWidget {
const ConfirmTaskScreen({super.key}); final String? selectedDistributorType;
const ConfirmTaskScreen({
super.key,
this.selectedDistributorType,
});
@override @override
State<ConfirmTaskScreen> createState() => _ConfirmTaskScreenState(); State<ConfirmTaskScreen> createState() => _ConfirmTaskScreenState();
@ -136,7 +140,8 @@ class _ConfirmTaskScreenState extends State<ConfirmTaskScreen> {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (taskProvider.selectedTask == 'Collect KYC') if (taskProvider.selectedTask == 'Collect KYC' ||
taskProvider.selectedTask == 'Visit RD/PD')
_customContainer( _customContainer(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -160,6 +165,31 @@ class _ConfirmTaskScreenState extends State<ConfirmTaskScreen> {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (taskProvider.selectedTask == 'Update Inventory Data' ||
taskProvider.selectedTask == 'Visit RD/PD') ...{
_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( CommonElevatedButton(
isLoading: taskProvider.isLoading, isLoading: taskProvider.isLoading,
backgroundColor: const Color(0xff004791), backgroundColor: const Color(0xff004791),
@ -167,7 +197,13 @@ class _ConfirmTaskScreenState extends State<ConfirmTaskScreen> {
width: double.infinity, width: double.infinity,
height: kToolbarHeight - 10, height: kToolbarHeight - 10,
text: 'CONFIRM', text: 'CONFIRM',
onPressed: () => taskProvider.assignTask(context), onPressed: () => taskProvider.assignTask(
context: context,
selectedDistributorType:
widget.selectedDistributorType != null
? widget.selectedDistributorType!
: '',
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
CommonElevatedButton( CommonElevatedButton(

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

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,9 +12,10 @@ class ApiUrls {
static const String notificationUrl = '$baseUrl/get-notification-tm'; static const String notificationUrl = '$baseUrl/get-notification-tm';
static const String fcmUrl = '${baseUrl}kyc/save-fcm-tm'; static const String fcmUrl = '${baseUrl}kyc/save-fcm-tm';
static const String getProducts = '${baseUrl}product/getAll/user/'; static const String getProducts = '${baseUrl}product/getAll/user/';
static const String getPd = 'inventory/distributors-TM/RetailDistributor'; static const String getRd = 'inventory/distributors-TM/RetailDistributor';
static const String getRd = 'inventory/distributors-TM/PrincipalDistributor'; static const String getPd = 'inventory/distributors-TM/PrincipalDistributor';
static const String submitProducts = 'inventory/add-TM'; static const String submitProducts = 'inventory/add-TM';
static const String getSalesCoordinators = 'salescoordinator/getAll-TM'; static const String getSalesCoordinators = 'salescoordinator/getAll-TM';
static const String assignTask = 'task/assign-task'; static const String assignTask = 'task/assign-task';
static const String getProductsManual = 'productmanual/getall';
} }

View File

@ -8,6 +8,7 @@
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_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) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = 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 = g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); 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 list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux file_selector_linux
flutter_secure_storage_linux flutter_secure_storage_linux
url_launcher_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,6 +5,7 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import device_info_plus
import file_selector_macos import file_selector_macos
import firebase_analytics import firebase_analytics
import firebase_core import firebase_core
@ -14,8 +15,11 @@ import flutter_local_notifications
import flutter_secure_storage_macos import flutter_secure_storage_macos
import geolocator_apple import geolocator_apple
import path_provider_foundation import path_provider_foundation
import syncfusion_pdfviewer_macos
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
@ -25,4 +29,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 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" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" 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: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1125,6 +1141,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" 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: table_calendar:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1189,6 +1269,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" 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: uuid:
dependency: transitive dependency: transitive
description: description:
@ -1277,6 +1421,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.3" 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: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -51,6 +51,7 @@ dependencies:
firebase_crashlytics: ^4.0.4 firebase_crashlytics: ^4.0.4
firebase_analytics: ^11.2.1 firebase_analytics: ^11.2.1
searchfield: ^1.0.9 searchfield: ^1.0.9
syncfusion_flutter_pdfviewer: ^26.2.11
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -11,6 +11,8 @@
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <geolocator_windows/geolocator_windows.h> #include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.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) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
@ -23,4 +25,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("GeolocatorWindows")); registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar( PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); 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 flutter_secure_storage_windows
geolocator_windows geolocator_windows
permission_handler_windows permission_handler_windows
syncfusion_pdfviewer_windows
url_launcher_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST