From 65529c16ed21ad73038bdfba66348353ed69f257 Mon Sep 17 00:00:00 2001 From: kratikpal Date: Mon, 5 Aug 2024 12:59:25 +0530 Subject: [PATCH] KYC --- lib/models/get_pd_response.dart | 73 ++++ lib/provider/collect_kyc_provider.dart | 327 ++++++++++++++---- .../assign_task_dash_board_screen.dart | 169 +++++++++ lib/screens/assign_task_screen.dart | 155 --------- lib/screens/assign_tasks_screen.dart | 307 ++++++++++++++++ lib/screens/collect_kyc_screen.dart | 118 ++++--- lib/screens/home_screen.dart | 5 +- lib/screens/login_screen.dart | 2 + lib/screens/retailer_details_screen.dart | 140 +++++--- .../select_sales_coordinator_screen.dart | 165 +++++++++ lib/screens/upload_documents_screen.dart | 177 ++++------ .../verification_documents_screen.dart | 88 +++-- lib/services/api_client.dart | 61 ++-- lib/services/api_urls.dart | 2 + lib/widgets/common_text_form_field.dart | 3 + pubspec.lock | 8 + pubspec.yaml | 1 + 17 files changed, 1308 insertions(+), 493 deletions(-) create mode 100644 lib/models/get_pd_response.dart create mode 100644 lib/screens/assign_task_dash_board_screen.dart delete mode 100644 lib/screens/assign_task_screen.dart create mode 100644 lib/screens/assign_tasks_screen.dart create mode 100644 lib/screens/select_sales_coordinator_screen.dart diff --git a/lib/models/get_pd_response.dart b/lib/models/get_pd_response.dart new file mode 100644 index 0000000..29c1d9d --- /dev/null +++ b/lib/models/get_pd_response.dart @@ -0,0 +1,73 @@ +class GetPdResponse { + Avatar? avatar; + String? sId; + String? name; + String? email; + String? phone; + String? role; + String? uniqueId; + String? createdAt; + String? updatedAt; + int? iV; + + GetPdResponse( + {this.avatar, + this.sId, + this.name, + this.email, + this.phone, + this.role, + this.uniqueId, + this.createdAt, + this.updatedAt, + this.iV}); + + GetPdResponse.fromJson(Map json) { + avatar = json['avatar'] != null ? Avatar.fromJson(json['avatar']) : null; + sId = json['_id']; + name = json['name']; + email = json['email']; + phone = json['phone']; + role = json['role']; + uniqueId = json['uniqueId']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + iV = json['__v']; + } + + Map toJson() { + final Map data = {}; + if (avatar != null) { + data['avatar'] = avatar!.toJson(); + } + data['_id'] = sId; + data['name'] = name; + data['email'] = email; + data['phone'] = phone; + data['role'] = role; + data['uniqueId'] = uniqueId; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = iV; + return data; + } +} + +class Avatar { + String? publicId; + String? url; + + Avatar({this.publicId, this.url}); + + Avatar.fromJson(Map json) { + publicId = json['public_id']; + url = json['url']; + } + + Map toJson() { + final Map data = {}; + data['public_id'] = publicId; + data['url'] = url; + return data; + } +} diff --git a/lib/provider/collect_kyc_provider.dart b/lib/provider/collect_kyc_provider.dart index 0837e80..4448adf 100644 --- a/lib/provider/collect_kyc_provider.dart +++ b/lib/provider/collect_kyc_provider.dart @@ -1,80 +1,281 @@ +import 'dart:io'; + +import 'package:cheminova/models/get_pd_response.dart'; +import 'package:cheminova/screens/data_submit_successfull.dart'; +import 'package:cheminova/services/api_client.dart'; +import 'package:cheminova/services/secure__storage_service.dart'; +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../services/api_urls.dart'; class CollectKycProvider extends ChangeNotifier { - late TabController tabController; + CollectKycProvider() { + getPd(); + } + TextEditingController country = TextEditingController(text: "India"); + TextEditingController state = TextEditingController(); + TextEditingController city = TextEditingController(); + + late TabController tabController; + final _apiClient = ApiClient(); final tradeNameController = TextEditingController(); final nameController = TextEditingController(); final addressController = TextEditingController(); final townCityController = TextEditingController(); final districtController = TextEditingController(); - final stateController = TextEditingController(); final pinCodeController = TextEditingController(); final mobileNumberController = TextEditingController(); final aadharNumberController = TextEditingController(); final panNumberController = TextEditingController(); final gstNumberController = TextEditingController(); + String? selectedDistributor; - final List distributors = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorIds = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorNames = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorAddresses = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorMobileNumbers = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorEmails = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorGstNumbers = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorPanNumbers = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorAadharNumbers = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; - final List distributorPinCodes = [ - 'Distributor 1', - 'Distributor 2', - 'Distributor 3', - 'Distributor 4', - ]; final retailerDetailsFormKey = GlobalKey(); -} + + File? panCard; + File? aadharCard; + File? gstRegistration; + File? pesticideLicense; + File? fertilizerLicense; + File? selfieEntranceBoard; + + final ImagePicker _picker = ImagePicker(); + + Future pickImage(ImageSource source, String documentType) async { + final pickedFile = await _picker.pickImage(source: source); + + if (pickedFile != null) { + switch (documentType) { + case 'PAN Card': + panCard = File(pickedFile.path); + break; + case 'Aadhar Card': + aadharCard = File(pickedFile.path); + break; + case 'GST Registration': + gstRegistration = File(pickedFile.path); + break; + case 'Pesticide License': + pesticideLicense = File(pickedFile.path); + break; + case 'Fertilizer License': + fertilizerLicense = File(pickedFile.path); + break; + case 'Selfie of Entrance Board': + selfieEntranceBoard = File(pickedFile.path); + break; + } + } + notifyListeners(); + } + + void showPicker(BuildContext context, String documentType) { + showModalBottomSheet( + context: context, + builder: (BuildContext bc) { + return SafeArea( + child: Wrap( + children: [ + if (documentType != 'Selfie of Entrance Board') + ListTile( + leading: const Icon(Icons.photo_library), + title: const Text('Photo Library'), + onTap: () { + pickImage(ImageSource.gallery, documentType); + Navigator.of(context).pop(); + }, + ), + ListTile( + leading: const Icon(Icons.photo_camera), + title: const Text('Camera'), + onTap: () { + pickImage(ImageSource.camera, documentType); + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + }); + } + + // Add methods for state and city selection if needed + void selectState(String selectedState) { + state.text = selectedState; + notifyListeners(); + } + + void selectCity(String selectedCity) { + city.text = selectedCity; + notifyListeners(); + } + + bool _isLoading = false; + + bool get isLoading => _isLoading; + List pdList = []; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + Future getPd() async { + setLoading(true); + try { + Response response = await _apiClient.get(ApiUrls.getPdUrl); + setLoading(false); + if (response.statusCode == 200) { + pdList = (response.data as List) + .map((e) => GetPdResponse.fromJson(e)) + .toList(); + + print('pd list length: ${pdList.length}'); + } else {} + } catch (e) { + setLoading(false); + } + } + + void validateFields(BuildContext context) { + if (tradeNameController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Trade Name is required')), + ); + } else if (nameController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Name is required')), + ); + } else if (addressController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Address is required')), + ); + } else if (city.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Town/City is required')), + ); + } else if (districtController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('District is required')), + ); + } else if (state.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('State is required')), + ); + } else if (pinCodeController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Pincode is required')), + ); + } else if (mobileNumberController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Mobile Number is required')), + ); + } else if (aadharNumberController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Aadhar Number is required')), + ); + } else if (panNumberController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('PAN Number is required')), + ); + } else if (gstNumberController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('GST Number is required')), + ); + } else if ((selectedDistributor ?? '').isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Mapped Principal Distributor is required')), + ); + } else if (panCard == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('PAN Card is required')), + ); + } else if (aadharCard == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Aadhar Card is required')), + ); + } else if (gstRegistration == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('GST Registration is required')), + ); + } else if (pesticideLicense == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Pesticide License is required')), + ); + } else if (selfieEntranceBoard == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Selfie of Entrance Board is required')), + ); + } else { + submitCollectKycForm(context); + } + } + + Future submitCollectKycForm(BuildContext context) async { + final dio = Dio(); + final token = await SecureStorageService().read(key: 'access_token'); + final headers = {'Authorization': 'Bearer $token'}; + + // Construct the FormData + final data = FormData.fromMap({ + 'name': nameController.text.trim(), + 'trade_name': tradeNameController.text.trim(), + 'address': addressController.text.trim(), + 'state': state.text.trim(), + 'city': city.text.trim(), + 'district': districtController.text.trim(), + 'pincode': pinCodeController.text.trim(), + 'mobile_number': mobileNumberController.text.trim(), + 'principal_distributer': selectedDistributor, + 'pan_number': panNumberController.text.trim(), + 'aadhar_number': aadharNumberController.text.trim(), + 'gst_number': gstNumberController.text.trim(), + if (panCard != null) + 'pan_img': await MultipartFile.fromFile(panCard!.path, filename: 'pan_card.jpg'), + if (aadharCard != null) + 'aadhar_img': await MultipartFile.fromFile(aadharCard!.path, filename: 'aadhar_card.jpg'), + if (gstRegistration != null) + 'gst_img': await MultipartFile.fromFile(gstRegistration!.path, filename: 'gst_registration.jpg'), + if (pesticideLicense != null) + 'pesticide_license_img': await MultipartFile.fromFile(pesticideLicense!.path, filename: 'pesticide_license.jpg'), + if (selfieEntranceBoard != null) + 'selfie_entrance_img': await MultipartFile.fromFile(selfieEntranceBoard!.path, filename: 'selfie_entrance_board.jpg'), + }); + + setLoading(true); + try { + + Response response = await _apiClient.post(ApiUrls.createCollectKycUrl, + data: data); + + setLoading(false); + + if (response.data['success'] == true) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(response.data['message'].toString()))); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const DataSubmitSuccessfull())); + } + } else { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Submission failed: ${response.statusMessage}'))); + } + } + } catch (e) { + setLoading(false); + if (context.mounted) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text('An error occurred: $e'))); + } + } + } +} \ No newline at end of file diff --git a/lib/screens/assign_task_dash_board_screen.dart b/lib/screens/assign_task_dash_board_screen.dart new file mode 100644 index 0000000..0ab4584 --- /dev/null +++ b/lib/screens/assign_task_dash_board_screen.dart @@ -0,0 +1,169 @@ +import 'package:cheminova/screens/select_sales_coordinator_screen.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'; + +class AssignTaskDashBoardScreen extends StatefulWidget { + const AssignTaskDashBoardScreen({super.key}); + + @override + State createState() => + _AssignTaskDashBoardScreenState(); +} + +class _AssignTaskDashBoardScreenState extends State { + @override + Widget build(BuildContext context) { + 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( + 'Assign Tasks', + style: TextStyle(color: Colors.black87, fontSize: 20), + ), + ), + ), + drawer: const CommonDrawer(), + body: CommonBackground( + child: SafeArea( + 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', + ), + ), + 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"), + ), + ], + ), + CommonElevatedButton( + backgroundColor: const Color(0xff004791), + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'ASSIGN TASKS', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const SelectSalesCoordinatorScreen(), + ), + ); + }, + ), + const SizedBox(height: 15), + CommonElevatedButton( + backgroundColor: const Color(0xff004791), + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'VIEW TASK STATUS', + onPressed: () {}, + ), + const SizedBox(height: 15), + CommonElevatedButton( + backgroundColor: const Color(0xff004791), + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'MANAGE SCs', + onPressed: () {}, + ), + ], + ), + ), + ), + ), + ), + ); + } + + Widget _customCard({required String title, required String subtitle}) { + return Container( + padding: const EdgeInsets.all(8.0).copyWith(top: 15, bottom: 30), + margin: const EdgeInsets.all(4).copyWith(bottom: 30), + 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, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 10), + Text( + subtitle, + style: const TextStyle( + fontSize: 34, + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: 'Anek', + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/assign_task_screen.dart b/lib/screens/assign_task_screen.dart deleted file mode 100644 index c43c1c3..0000000 --- a/lib/screens/assign_task_screen.dart +++ /dev/null @@ -1,155 +0,0 @@ -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'; - -class AssignTaskScreen extends StatefulWidget { - const AssignTaskScreen({super.key}); - - @override - State createState() => _AssignTaskScreenState(); -} - -class _AssignTaskScreenState extends State { - @override - Widget build(BuildContext context) { - 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( - 'Assign Tasks', - style: TextStyle(color: Colors.black87, fontSize: 20), - ), - ), - ), - drawer: const CommonDrawer(), - body: CommonBackground( - child: SafeArea( - 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', - ), - ), - 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"), - ), - ], - ), - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'ASSIGN TASKS', - onPressed: () {}, - ), - const SizedBox(height: 15), - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'VIEW TASK STATUS', - onPressed: () {}, - ), - const SizedBox(height: 15), - CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'MANAGE SCs', - onPressed: () {}, - ), - ], - ), - ), - ), - ), - ); - } - - Widget _customCard({required String title, required String subtitle}) { - return Container( - padding: const EdgeInsets.all(8.0).copyWith(top: 15, bottom: 30), - margin: const EdgeInsets.all(4).copyWith(bottom: 30), - 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, - children: [ - Text( - title, - style: const TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.bold, - fontFamily: 'Anek', - ), - ), - const SizedBox(height: 10), - Text( - subtitle, - style: const TextStyle( - fontSize: 34, - color: Colors.white, - fontWeight: FontWeight.bold, - fontFamily: 'Anek', - ), - ), - ], - ), - ); - } -} diff --git a/lib/screens/assign_tasks_screen.dart b/lib/screens/assign_tasks_screen.dart new file mode 100644 index 0000000..ef1b72f --- /dev/null +++ b/lib/screens/assign_tasks_screen.dart @@ -0,0 +1,307 @@ +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'; + +class AssignTasksScreen extends StatefulWidget { + const AssignTasksScreen({super.key}); + + @override + State createState() => _AssignTasksScreenState(); +} + +class _AssignTasksScreenState extends State { + final List _isChecked = List.generate(8, (_) => false); + + String _dateSelected = DateFormat('dd/MM/yyyy').format(DateTime.now()); + + Future _selectDate(BuildContext context) async { + final dateSelected = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime(2025), + ); + if (dateSelected != null) { + setState(() { + _dateSelected = DateFormat('dd/MM/yyyy').format(dateSelected); + }); + } + } + + @override + Widget build(BuildContext context) { + 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( + 'Assign Tasks', + 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( + 'Assign Tasks', + style: TextStyle( + fontSize: 24, + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: 'Anek', + ), + ), + 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', + ), + ), + Text( + "ID: 121", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + fontFamily: 'Anek', + ), + ), + ], + ), + ), + 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', + ), + ), + SizedBox( + height: 35, + child: CheckboxListTile( + value: _isChecked[0], + onChanged: (value) { + setState(() { + _isChecked[0] = value!; + }); + }, + title: const Text( + "Visit Retailers", + ), + ), + ), + SizedBox( + height: 45, + child: CheckboxListTile( + value: _isChecked[1], + onChanged: (value) { + setState(() { + _isChecked[1] = value!; + }); + }, + title: const Text( + "Update Sales Data", + ), + ), + ), + 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( + children: [ + Checkbox( + value: _isChecked[4], + onChanged: (value) { + setState(() { + _isChecked[4] = value!; + }); + }, + ), + const Text( + "Low", + ), + Checkbox( + value: _isChecked[5], + onChanged: (value) { + setState(() { + _isChecked[5] = value!; + }); + }, + ), + const Text( + "Medium", + ), + Checkbox( + value: _isChecked[6], + onChanged: (value) { + setState(() { + _isChecked[6] = value!; + }); + }, + ), + const Text( + "High", + ), + ], + ), + ], + ), + ), + 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: () {}, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/collect_kyc_screen.dart b/lib/screens/collect_kyc_screen.dart index b8610e6..a82529d 100644 --- a/lib/screens/collect_kyc_screen.dart +++ b/lib/screens/collect_kyc_screen.dart @@ -17,7 +17,10 @@ class CollectKycScreen extends StatefulWidget { State createState() => CollectKycScreenState(); } -class CollectKycScreenState extends State with SingleTickerProviderStateMixin{ +class CollectKycScreenState extends State + with SingleTickerProviderStateMixin { + late CollectKycProvider collectKycProvider; + final _pages = [ const RetailerDetailsScreen(), const UploadDocumentScreen(), @@ -26,7 +29,8 @@ class CollectKycScreenState extends State with SingleTickerPro @override void initState() { - Provider.of(context,listen: false).tabController = TabController(length: 3, vsync: this); + collectKycProvider = CollectKycProvider(); + collectKycProvider.tabController = TabController(length: 3, vsync: this); super.initState(); } @@ -132,57 +136,65 @@ class CollectKycScreenState extends State with SingleTickerPro @override Widget build(BuildContext context) { - return Consumer( - builder: (BuildContext context, value, Widget? child) => CommonBackground( - child: DefaultTabController( - length: 3, - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: PreferredSize( - preferredSize: const Size.fromHeight(100), - child: CommonAppBar( - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - title: const Text('Collect KYC Data', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - backgroundColor: Colors.transparent, - elevation: 0, - bottom: TabBar( - controller: value.tabController, - - padding: - const EdgeInsets.symmetric(horizontal: 10), - dividerColor: Colors.transparent, - indicatorColor: Colors.yellow, - labelColor: Colors.white, - unselectedLabelColor: Colors.black, - indicatorSize: TabBarIndicatorSize.tab, - indicator: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(10)), - tabs: const [ - Tab(text: 'Details'), - Tab(text: 'Documents'), - Tab(text: 'Verify') - ]))), - drawer: const CommonDrawer(), - body: TabBarView( - controller: value.tabController, - physics: const NeverScrollableScrollPhysics(), - children: _pages))) - - ), + return ChangeNotifierProvider( + create: (context) => collectKycProvider, + child: Consumer( + builder: (BuildContext context, value, Widget? child) => + Stack(children: [ + CommonBackground( + child: DefaultTabController( + length: 3, + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(100), + child: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: + Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: const Text('Collect KYC Data', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, + bottom: TabBar( + controller: value.tabController, + padding: const EdgeInsets.symmetric( + horizontal: 10), + dividerColor: Colors.transparent, + indicatorColor: Colors.yellow, + labelColor: Colors.white, + unselectedLabelColor: Colors.black, + indicatorSize: TabBarIndicatorSize.tab, + indicator: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(10)), + tabs: const [ + Tab(text: 'Details'), + Tab(text: 'Documents'), + Tab(text: 'Verify') + ]))), + drawer: const CommonDrawer(), + body: TabBarView( + controller: value.tabController, + physics: const NeverScrollableScrollPhysics(), + children: _pages)))), + if (value.isLoading) + Container( + color: Colors.black.withOpacity(0.5), + child: const Center(child: CircularProgressIndicator())) + ]), + ), ); } } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index c36159d..ab40c99 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,5 +1,5 @@ import 'package:cheminova/provider/user_provider.dart'; -import 'package:cheminova/screens/assign_task_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'; import 'package:cheminova/screens/mark_attendence_screen.dart'; @@ -111,7 +111,8 @@ class _HomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => const AssignTaskScreen(), + builder: (context) => + const AssignTaskDashBoardScreen(), )); }), const SizedBox( diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 3285b37..5bec42f 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -88,6 +88,7 @@ class _LoginPageState extends State { builder: (context, value, child) => CommonTextFormField( controller: value.emailController, + keyboardType: TextInputType.emailAddress, validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your email id'; @@ -105,6 +106,7 @@ class _LoginPageState extends State { builder: (context, value, child) => CommonTextFormField( controller: value.passwordController, + obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your password'; diff --git a/lib/screens/retailer_details_screen.dart b/lib/screens/retailer_details_screen.dart index fa9a3af..261ce59 100644 --- a/lib/screens/retailer_details_screen.dart +++ b/lib/screens/retailer_details_screen.dart @@ -1,5 +1,8 @@ +import 'package:cheminova/models/get_pd_response.dart'; import 'package:cheminova/provider/collect_kyc_provider.dart'; +import 'package:csc_picker/csc_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../widgets/common_elevated_button.dart'; @@ -72,16 +75,25 @@ class RetailerDetailsScreenState extends State { }, controller: value.addressController), const SizedBox(height: 15), - CommonTextFormField( - title: 'Town/City', - fillColor: Colors.white, - validator: (String? value) { - if (value!.isEmpty) { - return 'Town/City cannot be empty'; - } - return null; - }, - controller: value.townCityController), + CSCPicker( + defaultCountry: CscCountry.India, + disableCountry: true, + onCountryChanged: (val) { + setState(() { + value.country.text = val; + }); + }, + onStateChanged: (val) { + setState(() { + value.state.text = val ?? ''; + }); + }, + onCityChanged: (val) { + setState(() { + value.city.text = val ?? ''; + }); + }, + ), const SizedBox(height: 15), CommonTextFormField( title: 'District', @@ -95,20 +107,12 @@ class RetailerDetailsScreenState extends State { controller: value.districtController), const SizedBox(height: 15), CommonTextFormField( - title: 'State', - fillColor: Colors.white, - validator: (String? value) { - if (value!.isEmpty) { - return 'State cannot be empty'; - } - return null; - }, - controller: value.stateController), - const SizedBox(height: 15), - CommonTextFormField( - maxLength: 6, + maxLength: 6, title: 'Pincode', fillColor: Colors.white, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], validator: (String? value) { if (value!.isEmpty) { return 'Pincode cannot be empty'; @@ -118,9 +122,12 @@ class RetailerDetailsScreenState extends State { controller: value.pinCodeController), const SizedBox(height: 15), CommonTextFormField( - maxLength: 10, + maxLength: 10, title: 'Mobile Number', fillColor: Colors.white, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], validator: (String? value) { if (value!.isEmpty) { return 'Mobile Number cannot be empty'; @@ -130,8 +137,11 @@ class RetailerDetailsScreenState extends State { controller: value.mobileNumberController), const SizedBox(height: 15), CommonTextFormField( - maxLength: 12, + maxLength: 12, title: 'Aadhar Number', + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], fillColor: Colors.white, validator: (String? value) { if (value!.isEmpty) { @@ -142,7 +152,7 @@ class RetailerDetailsScreenState extends State { controller: value.aadharNumberController), const SizedBox(height: 15), CommonTextFormField( - maxLength: 10, + maxLength: 10, title: 'PAN Number', fillColor: Colors.white, validator: (String? value) { @@ -154,7 +164,7 @@ class RetailerDetailsScreenState extends State { controller: value.panNumberController), const SizedBox(height: 15), CommonTextFormField( - maxLength: 15, + maxLength: 15, title: 'GST Number', fillColor: Colors.white, validator: (String? value) { @@ -167,20 +177,16 @@ class RetailerDetailsScreenState extends State { const SizedBox(height: 15), DropdownButtonFormField( decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.0), - ), - labelText: 'Mapped Principal Distributor', - ), + fillColor: Colors.white, + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none), + hintText: 'Mapped Principal Distributor'), value: value.selectedDistributor, - items: - value.distributors.map((String distributor) { + items: value.pdList.map((GetPdResponse pd) { return DropdownMenuItem( - value: distributor, - child: Text(distributor), - ); + value: pd.sId, child: Text(pd.name ?? '')); }).toList(), onChanged: (String? newValue) { setState(() { @@ -188,7 +194,7 @@ class RetailerDetailsScreenState extends State { }); }, validator: (String? value) { - if (value!.isEmpty) { + if (value == null || value.isEmpty) { return 'Mapped Principal Distributor cannot be empty'; } return null; @@ -204,10 +210,10 @@ class RetailerDetailsScreenState extends State { text: 'CONTINUE', backgroundColor: const Color(0xff004791), onPressed: () { - // if (value.retailerDetailsFormKey.currentState! - // .validate()) { + if (value.retailerDetailsFormKey.currentState! + .validate()) { value.tabController.animateTo(1); - // } + } }, ), ), @@ -221,3 +227,53 @@ class RetailerDetailsScreenState extends State { )); } } + +class CustomCountryStateCityPicker extends StatelessWidget { + final TextEditingController country; + final TextEditingController state; + final TextEditingController city; + final Color dialogColor; + final InputDecoration textFieldDecoration; + + const CustomCountryStateCityPicker({ + super.key, + required this.country, + required this.state, + required this.city, + required this.dialogColor, + required this.textFieldDecoration, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TextFormField( + controller: country, + decoration: textFieldDecoration.copyWith( + labelText: 'Country', + enabled: false, + ), + ), + const SizedBox(height: 15), + TextFormField( + controller: state, + decoration: textFieldDecoration.copyWith(labelText: 'State'), + readOnly: true, + onTap: () { + // Implement state selection logic for India + }, + ), + const SizedBox(height: 15), + TextFormField( + controller: city, + decoration: textFieldDecoration.copyWith(labelText: 'City'), + readOnly: true, + onTap: () { + // Implement city selection logic based on selected state + }, + ), + ], + ); + } +} diff --git a/lib/screens/select_sales_coordinator_screen.dart b/lib/screens/select_sales_coordinator_screen.dart new file mode 100644 index 0000000..3ea5f51 --- /dev/null +++ b/lib/screens/select_sales_coordinator_screen.dart @@ -0,0 +1,165 @@ +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'; +import 'package:cheminova/widgets/common_text_form_field.dart'; +import 'package:flutter/material.dart'; + +class SelectSalesCoordinatorScreen extends StatefulWidget { + const SelectSalesCoordinatorScreen({super.key}); + + @override + State createState() => + _SelectSalesCoordinatorScreenState(); +} + +class _SelectSalesCoordinatorScreenState + extends State { + List salesCoordinators = [ + SalesCoordinatorModel(name: 'Sales Coordinator 1', id: '121', tasks: 2), + SalesCoordinatorModel(name: 'Sales Coordinator 2', id: '122', tasks: 3), + SalesCoordinatorModel(name: 'Sales Coordinator 3', id: '123', tasks: 4), + ]; + @override + Widget build(BuildContext context) { + 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( + 'Sales Coordinator', + style: TextStyle(color: Colors.black87, fontSize: 20), + ), + ), + ), + drawer: const CommonDrawer(), + body: CommonBackground( + child: SafeArea( + 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( + 'Select Sales Coordinator', + style: TextStyle( + fontSize: 24, + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 20), + const CommonTextFormField( + title: 'Search Sales Coordinator', + ), + const SizedBox(height: 20), + Expanded( + child: ListView.builder( + itemCount: salesCoordinators.length, + itemBuilder: (context, index) { + return _customCard( + name: salesCoordinators[index].name, + id: salesCoordinators[index].id, + tasks: salesCoordinators[index].tasks, + ); + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } + + Widget _customCard( + {required String name, required String id, required int tasks}) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const AssignTasksScreen(); + }, + ), + ); + }, + 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( + "Tasks: $tasks", + style: const TextStyle( + fontFamily: 'Anek', + fontSize: 16, + ), + ), + ], + ), + ), + ); + } +} + +class SalesCoordinatorModel { + final String name; + final String id; + final int tasks; + + SalesCoordinatorModel({ + required this.name, + required this.id, + required this.tasks, + }); +} diff --git a/lib/screens/upload_documents_screen.dart b/lib/screens/upload_documents_screen.dart index 63ad85b..c203ed1 100644 --- a/lib/screens/upload_documents_screen.dart +++ b/lib/screens/upload_documents_screen.dart @@ -1,12 +1,7 @@ import 'dart:io'; import 'package:cheminova/provider/collect_kyc_provider.dart'; -import 'package:cheminova/screens/data_submit_successfull.dart'; -import 'package:cheminova/widgets/common_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; -import '../widgets/common_app_bar.dart'; import '../widgets/common_elevated_button.dart'; class UploadDocumentScreen extends StatefulWidget { @@ -17,74 +12,6 @@ class UploadDocumentScreen extends StatefulWidget { } class UploadDocumentScreenState extends State { - File? _panCard; - File? _aadharCard; - File? _gstRegistration; - File? _pesticideLicense; - File? _fertilizerLicense; - File? _selfieEntranceBoard; - - final ImagePicker _picker = ImagePicker(); - - Future _pickImage(ImageSource source, String documentType) async { - final pickedFile = await _picker.pickImage(source: source); - - setState(() { - if (pickedFile != null) { - switch (documentType) { - case 'PAN Card': - _panCard = File(pickedFile.path); - break; - case 'Aadhar Card': - _aadharCard = File(pickedFile.path); - break; - case 'GST Registration': - _gstRegistration = File(pickedFile.path); - break; - case 'Pesticide License': - _pesticideLicense = File(pickedFile.path); - break; - case 'Fertilizer License': - _fertilizerLicense = File(pickedFile.path); - break; - case 'Selfie of Entrance Board': - _selfieEntranceBoard = File(pickedFile.path); - break; - } - } - }); - } - - void _showPicker(BuildContext context, String documentType) { - showModalBottomSheet( - context: context, - builder: (BuildContext bc) { - return SafeArea( - child: Wrap( - children: [ - if (documentType != 'Selfie of Entrance Board') - ListTile( - leading: const Icon(Icons.photo_library), - title: const Text('Photo Library'), - onTap: () { - _pickImage(ImageSource.gallery, documentType); - Navigator.of(context).pop(); - }, - ), - ListTile( - leading: const Icon(Icons.photo_camera), - title: const Text('Camera'), - onTap: () { - _pickImage(ImageSource.camera, documentType); - Navigator.of(context).pop(); - }, - ), - ], - ), - ); - }); - } - Widget _buildUploadButton(String title, File? file, bool isOptional) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -94,19 +21,42 @@ class UploadDocumentScreenState extends State { style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: file == null ? 'Upload $title' : 'Change $title', - backgroundColor: const Color(0xff004791), - onPressed: () => _showPicker(context, title), - ), + + /// Create a view for the selected file + if (file != null) + Container( + height: 100, + width: 100, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + child: file.path.endsWith('.jpg') || file.path.endsWith('.png') + ? Image.file(file, fit: BoxFit.cover) + : Center( + child: Text(file.path.split('/').last, + style: const TextStyle( + fontSize: 12, fontWeight: FontWeight.bold), + textAlign: TextAlign.center))) + else + const Text('No file selected', style: TextStyle(color: Colors.red)), + if (file != null) Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text('File uploaded: ${file.path.split('/').last}'), + padding: const EdgeInsets.only(top: 8.0), + child: Text('File uploaded: ${file.path.split('/').last}')), + + const SizedBox(height: 8), + Consumer( + builder: (context, value, child) => CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: file == null ? 'Upload $title' : 'Change $title', + backgroundColor: const Color(0xff004791), + onPressed: () => value.showPicker(context, title), ), + ), const SizedBox(height: 15), ], ); @@ -115,7 +65,7 @@ class UploadDocumentScreenState extends State { @override Widget build(BuildContext context) { return Consumer( - builder: (context, value, child) => Padding( + builder: (context, value, child) => Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( physics: const BouncingScrollPhysics(), @@ -125,35 +75,44 @@ class UploadDocumentScreenState extends State { children: [ const SizedBox(height: 16), Container( - padding: const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), + padding: + const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), 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: [ - _buildUploadButton('PAN Card', _panCard, false), - _buildUploadButton('Aadhar Card', _aadharCard, false), - _buildUploadButton('GST Registration', _gstRegistration, false), - _buildUploadButton('Pesticide License', _pesticideLicense, false), - _buildUploadButton('Fertilizer License', _fertilizerLicense, true), - _buildUploadButton('Selfie of Entrance Board', _selfieEntranceBoard, false), - const SizedBox(height: 30), - Align( - alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SUBMIT', - backgroundColor: const Color(0xff004791), - onPressed: () { - // Handle form submission - value.tabController.animateTo(2); }, + child: Consumer( + builder: (context, value, child) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildUploadButton('PAN Card', value.panCard, false), + _buildUploadButton( + 'Aadhar Card', value.aadharCard, false), + _buildUploadButton( + 'GST Registration', value.gstRegistration, false), + _buildUploadButton( + 'Pesticide License', value.pesticideLicense, false), + _buildUploadButton( + 'Fertilizer License', value.fertilizerLicense, true), + _buildUploadButton('Selfie of Entrance Board', + value.selfieEntranceBoard, false), + const SizedBox(height: 30), + Align( + alignment: Alignment.center, + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SUBMIT', + backgroundColor: const Color(0xff004791), + onPressed: () { + // Handle form submission + value.tabController.animateTo(2); + }, + ), ), - ), - ], + ], + ), ), ), ], @@ -162,4 +121,4 @@ class UploadDocumentScreenState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/verification_documents_screen.dart b/lib/screens/verification_documents_screen.dart index 87ef294..c03ec65 100644 --- a/lib/screens/verification_documents_screen.dart +++ b/lib/screens/verification_documents_screen.dart @@ -1,9 +1,8 @@ import 'package:cheminova/provider/collect_kyc_provider.dart'; -import 'package:flutter/material.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:cheminova/screens/data_submit_successfull.dart'; +import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class VerifyDocumentsScreen extends StatelessWidget { @@ -22,21 +21,24 @@ class VerifyDocumentsScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Consumer( - builder: (BuildContext context, CollectKycProvider value, Widget? child) => _buildSection( + builder: (BuildContext context, CollectKycProvider value, + Widget? child) => + _buildSection( 'Retailer Details', { 'Trade Name': value.tradeNameController.text, 'Name': value.nameController.text, 'Address': value.addressController.text, - 'Town/City': value.townCityController.text, + 'Town/City': value.city.text, 'District': value.districtController.text, - 'State': value.stateController.text, + 'State': value.state.text, 'Pincode': value.pinCodeController.text, 'Mobile Number': value.mobileNumberController.text, 'Aadhar Number': value.aadharNumberController.text, 'PAN Number': value.panNumberController.text, 'GST Number': value.gstNumberController.text, - 'Mapped Principal Distributor': value.selectedDistributor ?? '', + 'Mapped Principal Distributor': + value.selectedDistributor ?? '', }, onEdit: () { value.tabController.animateTo(0); @@ -47,15 +49,27 @@ class VerifyDocumentsScreen extends StatelessWidget { ), const SizedBox(height: 20), Consumer( - builder: (BuildContext context, CollectKycProvider value, Widget? child) => _buildSection( + builder: (BuildContext context, CollectKycProvider value, + Widget? child) => + _buildSection( 'Uploaded Documents', { - 'PAN Card': 'pan_card.pdf', - 'Aadhar Card': 'aadhar_card.pdf', - 'GST Registration': 'gst_registration.pdf', - 'Pesticide License': 'pesticide_license.pdf', - 'Fertilizer License': 'fertilizer_license.pdf (Optional)', - 'Selfie of Entrance Board': 'entrance_board_selfie.jpg', + 'PAN Card': + value.panCard?.path.split('/').last ?? 'Not uploaded', + 'Aadhar Card': value.aadharCard?.path.split('/').last ?? + 'Not uploaded', + 'GST Registration': + value.gstRegistration?.path.split('/').last ?? + 'Not uploaded', + 'Pesticide License': + value.pesticideLicense?.path.split('/').last ?? + 'Not uploaded', + 'Fertilizer License': + value.fertilizerLicense?.path.split('/').last ?? + 'Not uploaded', + 'Selfie of Entrance Board': + value.selfieEntranceBoard?.path.split('/').last ?? + 'Not uploaded', }, onEdit: () { value.tabController.animateTo(1); @@ -68,21 +82,19 @@ class VerifyDocumentsScreen extends StatelessWidget { const SizedBox(height: 30), Align( alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: ('SAVE AND CONFIRM'), - backgroundColor: const Color(0xff004791), - onPressed: () { - // Handle final submission - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const DataSubmitSuccessfull(), - ), - ); - }, + child: Consumer( + builder: (context, value, child) => CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: ('SAVE AND CONFIRM'), + backgroundColor: const Color(0xff004791), + onPressed: () { + // Handle final submission + + value.validateFields(context); + }, + ), ), ), ], @@ -93,7 +105,8 @@ class VerifyDocumentsScreen extends StatelessWidget { ); } - Widget _buildSection(String title, Map details, {required VoidCallback onEdit}) { + Widget _buildSection(String title, Map details, + {required VoidCallback onEdit}) { return Container( padding: const EdgeInsets.all(20.0), // margin: const EdgeInsets.symmetric(horizontal: 30.0), @@ -110,7 +123,8 @@ class VerifyDocumentsScreen extends StatelessWidget { children: [ Text( title, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: + const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ElevatedButton( onPressed: onEdit, @@ -126,7 +140,8 @@ class VerifyDocumentsScreen extends StatelessWidget { ], ), const SizedBox(height: 10), - ...details.entries.map((entry) => _buildDetailRow(entry.key, entry.value)), + ...details.entries + .map((entry) => _buildDetailRow(entry.key, entry.value)), ], ), ); @@ -146,11 +161,14 @@ class VerifyDocumentsScreen extends StatelessWidget { ), ), Expanded( - flex: 3, - child: Text(value), - ), + flex: 3, + child: Text( + value, + style: TextStyle( + color: value == 'Not uploaded' ? Colors.red : Colors.black), + )), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/services/api_client.dart b/lib/services/api_client.dart index 4e37514..749bdd0 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -8,49 +8,42 @@ class ApiClient { final SecureStorageService _storageService = SecureStorageService(); ApiClient({String? baseUrl}) - : _dio = Dio( - BaseOptions( + : _dio = Dio(BaseOptions( baseUrl: baseUrl ?? ApiUrls.baseUrl, connectTimeout: const Duration(seconds: 120), - receiveTimeout: const Duration(seconds: 120), - ), - ) { - _dio.interceptors.add( - InterceptorsWrapper( - onRequest: (options, handler) async { - String? token = await _storageService.read(key: 'access_token'); - if (token != null) { - options.headers['Authorization'] = 'Bearer $token'; - } - return handler.next(options); - }, - onResponse: (response, handler) { - return handler.next(response); - }, - onError: (DioException e, handler) { - return handler.next(e); - }, - ), - ); - + receiveTimeout: const Duration(seconds: 120))) { _dio.interceptors .add(LogInterceptor(responseBody: true, requestBody: true)); - _dio.interceptors.add(PrettyDioLogger( - requestHeader: true, - requestBody: true, - responseBody: true, - responseHeader: false, - error: true, - compact: true, - maxWidth: 90, - )); + _dio.interceptors + .add(InterceptorsWrapper(onRequest: (options, handler) async { + String? token = await _storageService.read(key: 'access_token'); + if (token != null) { + options.headers['Authorization'] = 'Bearer $token'; + } + _dio.interceptors + .add(LogInterceptor(responseBody: true, requestBody: true)); + _dio.interceptors.add(PrettyDioLogger( + requestHeader: true, + requestBody: true, + responseBody: true, + responseHeader: false, + error: true, + compact: true, + maxWidth: 90, + )); + return handler.next(options); + }, onResponse: (response, handler) { + return handler.next(response); + }, onError: (DioException e, handler) { + return handler.next(e); + })); } - Future get(String path, {Map? queryParameters}) { + Future get(String path, {Map? queryParameters}) { return _dio.get(path, queryParameters: queryParameters); } - Future post(String path, {Map? data}) { + Future post(String path, {dynamic data}) { return _dio.post(path, data: data); } diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index e182b29..d02e770 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -6,4 +6,6 @@ class ApiUrls { static const String markLeaveUrl = 'v1/markleave/territorymanager'; static const String forgetPasswordUrl = 'territorymanager/forgot-password'; static const String changePasswordUrl = 'territorymanager/password/update'; + static const String createCollectKycUrl = '${baseUrl}kyc/create-tm'; + static const String getPdUrl = 'kyc/get-pd-tm'; } diff --git a/lib/widgets/common_text_form_field.dart b/lib/widgets/common_text_form_field.dart index 68b370f..f21e149 100644 --- a/lib/widgets/common_text_form_field.dart +++ b/lib/widgets/common_text_form_field.dart @@ -12,6 +12,7 @@ class CommonTextFormField extends StatelessWidget { final TextInputType? keyboardType; final List? inputFormatters; final int? maxLength; + final bool obscureText; const CommonTextFormField({ super.key, @@ -25,6 +26,7 @@ class CommonTextFormField extends StatelessWidget { this.keyboardType, this.inputFormatters, this.maxLength, + this.obscureText = false, }); @override @@ -47,6 +49,7 @@ class CommonTextFormField extends StatelessWidget { validator: validator, keyboardType: keyboardType, inputFormatters: inputFormatters, + obscureText: obscureText, decoration: InputDecoration( hintText: title, contentPadding: const EdgeInsets.only(bottom: 10, left: 10), diff --git a/pubspec.lock b/pubspec.lock index 8dcdd7d..9157093 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -177,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + csc_picker: + dependency: "direct main" + description: + name: csc_picker + sha256: "7e5d6023a81f53b89a7fb0369953fd4dc676f7ea20e9a0c0707973a5f0aedf14" + url: "https://pub.dev" + source: hosted + version: "0.2.7" cupertino_icons: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3492b35..9e24874 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: permission_handler: ^11.3.1 geocoding: ^3.0.0 pretty_dio_logger: ^1.3.1 + csc_picker: ^0.2.7 dev_dependencies: flutter_test: