diff --git a/lib/main.dart b/lib/main.dart index 57a848e..8a6887e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ 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'; @@ -22,8 +23,10 @@ Future _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; @@ -102,6 +105,7 @@ Future main() async { ChangeNotifierProvider(create: (_) => ProductProvider()), ChangeNotifierProvider(create: (_) => PdRdProvider()), ChangeNotifierProvider(create: (_) => TaskProvider()), + ChangeNotifierProvider(create: (_) => ProductManualProvider()), ], child: const MyApp(), ), diff --git a/lib/models/pd_rd_response_model.dart b/lib/models/pd_rd_response_model.dart index 227c4c6..cd8f776 100644 --- a/lib/models/pd_rd_response_model.dart +++ b/lib/models/pd_rd_response_model.dart @@ -2,57 +2,17 @@ class PdRdResponseModel { String? id; String? uniqueId; String? name; - String? tradeName; - String? address; - String? state; - 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? notes; - DateTime? createdAt; - DateTime? updatedAt; - int? v; + String? tradeNameRd; + ShippingAddress? shippingAddress; + String? salesCoordinator; // Nullable property for SalesCoordinator PdRdResponseModel({ this.id, this.uniqueId, this.name, - this.tradeName, - this.address, - this.state, - 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, + this.tradeNameRd, + this.shippingAddress, + this.salesCoordinator, // Initialize SalesCoordinator }); factory PdRdResponseModel.fromJson(Map json) => @@ -60,101 +20,53 @@ class PdRdResponseModel { id: json["_id"], name: json["name"], uniqueId: json["uniqueId"], - // tradeName: json["trade_name"], - // address: json["address"], - // state: json["state"], - // city: json["city"], - // district: json["district"], - // pincode: json["pincode"], - // mobileNumber: json["mobile_number"], - // 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.from(json["notes"].map((x) => Note.fromJson(x))), - // createdAt: DateTime.parse(json["createdAt"]), - // updatedAt: DateTime.parse(json["updatedAt"]), - // v: json["__v"], + tradeNameRd: json["trade_name"], + shippingAddress: json['shippingAddress'] != null + ? ShippingAddress.fromJson(json['shippingAddress']) + : null, + salesCoordinator: json.containsKey('salesCoordinator') + ? json['salesCoordinator'] + : null, // Handle missing field ); - - Map 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.from(notes.map((x) => x.toJson())), - // "createdAt": createdAt.toIso8601String(), - // "updatedAt": updatedAt.toIso8601String(), - // "__v": v, - }; } -class ImageModel { - String? publicId; - String? url; +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; - ImageModel({ - this.publicId, - this.url, + 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 ImageModel.fromJson(Map json) => ImageModel( - publicId: json["public_id"], - url: json["url"], - ); - - Map toJson() => { - "public_id": publicId, - "url": url, - }; -} - -class Note { - String? message; - DateTime? replyDate; - String? id; - - Note({ - this.message, - this.replyDate, - this.id, - }); - - factory Note.fromJson(Map json) => Note( - message: json["message"], - replyDate: DateTime.parse(json["replyDate"]), - id: json["_id"], - ); - - Map toJson() => { - "message": message, - // "replyDate": replyDate.toIso8601String(), - "_id": id, - }; + factory ShippingAddress.fromJson(Map 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, + ); + } } diff --git a/lib/models/product_manual_model.dart b/lib/models/product_manual_model.dart new file mode 100644 index 0000000..26b4451 --- /dev/null +++ b/lib/models/product_manual_model.dart @@ -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 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 json) { + return ProductManualDetail( + publicId: json['public_id'], + url: json['url'], + filename: json['filename'], + ); + } +} diff --git a/lib/models/product_model.dart b/lib/models/product_model.dart index 1adf0c3..7ea9986 100644 --- a/lib/models/product_model.dart +++ b/lib/models/product_model.dart @@ -37,10 +37,11 @@ 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 image; @@ -55,10 +56,11 @@ class Product { 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, @@ -75,10 +77,11 @@ 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, @@ -96,10 +99,11 @@ 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, @@ -136,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 json) { - return GST( + factory Brand.fromJson(Map json) { + return Brand( id: json['_id'], - name: json['name'], - tax: json['tax'], + brandName: json['brandName'], ); } Map toJson() { return { '_id': id, - 'name': name, - 'tax': tax, + 'brandName': brandName, }; } } diff --git a/lib/provider/product_manual_provider.dart b/lib/provider/product_manual_provider.dart new file mode 100644 index 0000000..d0e83f3 --- /dev/null +++ b/lib/provider/product_manual_provider.dart @@ -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 _productManualList = []; + final _apiClient = ApiClient(); + + List get productManualList => _productManualList; + bool get isLoading => _isLoading; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + Future getProductManualList() async { + setLoading(true); + try { + Response response = await _apiClient.get(ApiUrls.getProductsManual); + + if (response.statusCode == 200) { + List data = + (response.data['productManuals'] as List) + .map((json) => ProductManualModel.fromJson(json)) + .toList(); + _productManualList = data; + } + } catch (e) { + print("Error occurred: $e"); + } finally { + setLoading(false); + } + } +} diff --git a/lib/provider/task_provider.dart b/lib/provider/task_provider.dart index aafbc97..b682f87 100644 --- a/lib/provider/task_provider.dart +++ b/lib/provider/task_provider.dart @@ -13,6 +13,9 @@ class TaskProvider extends ChangeNotifier { String? _selectedPriority; String _selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now()); List _salesCoordinators = []; + List _pdList = []; + List _rdList = []; + PdRdResponseModel? _selectedDistributor; final TextEditingController _noteController = TextEditingController(); final _apiClient = ApiClient(); @@ -23,6 +26,9 @@ class TaskProvider extends ChangeNotifier { String get selectedDate => _selectedDate; List get salesCoordinators => _salesCoordinators; TextEditingController get noteController => _noteController; + List get pdList => _pdList; + List get rdList => _rdList; + PdRdResponseModel? get selectedDistributor => _selectedDistributor; void setLoading(bool loading) { _isLoading = loading; @@ -49,7 +55,7 @@ class TaskProvider extends ChangeNotifier { notifyListeners(); } - void clear(){ + void clear() { _selectedSalesCoordinator = null; _selectedTask = null; _selectedPriority = null; @@ -79,19 +85,39 @@ class TaskProvider extends ChangeNotifier { } } - Future assignTask(BuildContext context) async { + Future assignTask({ + required BuildContext context, + String? selectedDistributorType, + }) async { setLoading(true); + print("addedFff:$selectedDistributorType"); + 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: { - 'taskAssignedTo': _selectedSalesCoordinator!.id, - 'task': _selectedTask, - 'taskPriority': _selectedPriority, - 'taskDueDate': _selectedDate, - 'note': _noteController.text, - }, + data: data, ); + if (response.statusCode == 201) { Navigator.push( context, @@ -108,4 +134,57 @@ class TaskProvider extends ChangeNotifier { setLoading(false); } } + + void clearLists() { + _pdList.clear(); + _rdList.clear(); + notifyListeners(); + } + + void setSelectedDistributor(PdRdResponseModel? distributor) { + _selectedDistributor = distributor; + notifyListeners(); + } + + Future 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 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 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 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); + } + } } diff --git a/lib/screens/assign_tasks_screen.dart b/lib/screens/assign_tasks_screen.dart index 63adb7d..7767ded 100644 --- a/lib/screens/assign_tasks_screen.dart +++ b/lib/screens/assign_tasks_screen.dart @@ -1,3 +1,4 @@ +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'; @@ -21,6 +22,8 @@ class _AssignTasksScreenState extends State { bool _isTaskValid = true; bool _isPriorityValid = true; bool _isNotesValid = true; + bool _isDistributorValid = true; + String? selectedDistributorType; void _validateAndSubmit() { final taskProvider = context.read(); @@ -37,18 +40,37 @@ class _AssignTasksScreenState extends State { taskProvider.selectedPriority!.isNotEmpty; // Validate Notes - if (taskProvider.selectedTask == 'Collect KYC' && + if ((taskProvider.selectedTask == 'Collect KYC' || + taskProvider.selectedTask == 'Visit RD/PD') && taskProvider.noteController.text.isEmpty) { _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 (_isSalesCoordinatorValid && _isTaskValid && _isPriorityValid) { + if (_isSalesCoordinatorValid && + _isTaskValid && + _isPriorityValid && + _isNotesValid && + _isDistributorValid) { Navigator.push( context, MaterialPageRoute( - builder: (context) => const ConfirmTaskScreen(), + builder: (context) => ConfirmTaskScreen( + selectedDistributorType: selectedDistributorType, + ), ), ); } @@ -225,13 +247,13 @@ class _AssignTasksScreenState extends State { height: 35, child: RadioListTile( contentPadding: EdgeInsets.zero, - value: 'Visit Retailers', + value: 'Visit RD/PD', groupValue: taskProvider.selectedTask, onChanged: (value) { taskProvider.setSelectedTask(value); }, title: const Text( - "Visit Retailers", + "Visit RD/PD", ), ), ), @@ -253,13 +275,13 @@ class _AssignTasksScreenState extends State { height: 45, child: RadioListTile( contentPadding: EdgeInsets.zero, - value: 'Update Liqudation Data', + value: 'Update Inventory Data', groupValue: taskProvider.selectedTask, onChanged: (value) { taskProvider.setSelectedTask(value); }, title: const Text( - "Update Liqudation Data", + "Update Inventory Data", ), ), ), @@ -290,7 +312,8 @@ class _AssignTasksScreenState extends State { ], ), ), - if (taskProvider.selectedTask == 'Collect KYC') ...{ + if (taskProvider.selectedTask == 'Collect KYC' || + taskProvider.selectedTask == 'Visit RD/PD') ...{ Container( height: 150, decoration: BoxDecoration( @@ -320,6 +343,88 @@ class _AssignTasksScreenState extends State { 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( + decoration: const InputDecoration( + labelText: 'Select Distributor Type', + fillColor: Colors.white, + filled: true, + border: OutlineInputBorder(), + ), + value: selectedDistributorType, + items: [ + 'PrincipalDistributor', + 'RetailDistributor' + ].map((String type) { + return DropdownMenuItem( + 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( + 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( + 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( width: double.infinity, padding: const EdgeInsets.all(12.0), diff --git a/lib/screens/confirm_task_screen.dart b/lib/screens/confirm_task_screen.dart index 9c3b58a..3357d51 100644 --- a/lib/screens/confirm_task_screen.dart +++ b/lib/screens/confirm_task_screen.dart @@ -7,7 +7,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class ConfirmTaskScreen extends StatefulWidget { - const ConfirmTaskScreen({super.key}); + final String? selectedDistributorType; + const ConfirmTaskScreen({ + super.key, + this.selectedDistributorType, + }); @override State createState() => _ConfirmTaskScreenState(); @@ -136,7 +140,8 @@ class _ConfirmTaskScreenState extends State { ), ), const SizedBox(height: 20), - if (taskProvider.selectedTask == 'Collect KYC') + if (taskProvider.selectedTask == 'Collect KYC' || + taskProvider.selectedTask == 'Visit RD/PD') _customContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -160,6 +165,31 @@ class _ConfirmTaskScreenState extends State { ), ), 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( isLoading: taskProvider.isLoading, backgroundColor: const Color(0xff004791), @@ -167,7 +197,13 @@ class _ConfirmTaskScreenState extends State { width: double.infinity, height: kToolbarHeight - 10, text: 'CONFIRM', - onPressed: () => taskProvider.assignTask(context), + onPressed: () => taskProvider.assignTask( + context: context, + selectedDistributorType: + widget.selectedDistributorType != null + ? widget.selectedDistributorType! + : '', + ), ), const SizedBox(height: 20), CommonElevatedButton( diff --git a/lib/screens/products_manual_screen.dart b/lib/screens/products_manual_screen.dart index 84a7f63..b779b39 100644 --- a/lib/screens/products_manual_screen.dart +++ b/lib/screens/products_manual_screen.dart @@ -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(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: [ 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: [ - _buildProductButton('Product 1'), - _buildProductButton('Product 2'), - _buildProductButton('Product 3'), - _buildProductButton('Product 4'), - _buildProductButton('Product 5'), - _buildProductButton('Product 6'), - _buildProductButton('Product 7'), - ], - ), + Consumer( + 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), + ), + ), ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/view_pdf_screen.dart b/lib/screens/view_pdf_screen.dart new file mode 100644 index 0000000..6eea4d2 --- /dev/null +++ b/lib/screens/view_pdf_screen.dart @@ -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 createState() => _ViewPdfScreenState(); +} + +class _ViewPdfScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SfPdfViewer.network( + widget.productManualModel.productManualDetail.url, + ), + ), + ); + } +} diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index 8a50745..e22dbd8 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -12,9 +12,10 @@ 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 getPd = 'inventory/distributors-TM/RetailDistributor'; - static const String getRd = 'inventory/distributors-TM/PrincipalDistributor'; + 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'; } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 85a2413..3ccd551 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include 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); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 62e3ed5..9ce94c4 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -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 diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 9efb22b..36ffc87 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -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")) } diff --git a/pubspec.lock b/pubspec.lock index 8fa01e5..8af8eb8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: @@ -1125,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: @@ -1189,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: @@ -1277,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: diff --git a/pubspec.yaml b/pubspec.yaml index 22d64cc..62ee9ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: firebase_crashlytics: ^4.0.4 firebase_analytics: ^11.2.1 searchfield: ^1.0.9 + syncfusion_flutter_pdfviewer: ^26.2.11 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 41dfcd2..8592777 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,8 @@ #include #include #include +#include +#include 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")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 9c3f356..a10b9c8 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -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