From c30a9b7661abb1e74f71a53ae94390d59269b107 Mon Sep 17 00:00:00 2001 From: Vaibhav Date: Wed, 31 Jul 2024 20:10:13 +0530 Subject: [PATCH] -- kyc api and change password api has been integrated --- lib/main.dart | 9 +- lib/models/get_pd_response.dart | 74 ++++ lib/provider/attendance_provider.dart | 13 +- lib/provider/change_password_provider.dart | 51 +++ lib/provider/collect_kyc_provider.dart | 325 ++++++++++++++---- lib/provider/home_provider.dart | 33 ++ lib/screens/change_password_screen.dart | 274 ++++++++------- lib/screens/collect_kyc_screen.dart | 118 ++++--- lib/screens/forgot_password_screen.dart | 156 +++++---- lib/screens/home_screen.dart | 189 ++++++---- lib/screens/login_screen.dart | 46 +-- lib/screens/retailer_details_screen.dart | 141 +++++--- lib/screens/splash_screen.dart | 15 +- lib/screens/upload_documents_screen.dart | 172 ++++----- .../verification_documents_screen.dart | 91 +++-- lib/services/api_client.dart | 4 +- lib/services/api_urls.dart | 9 +- lib/widgets/common_drawer.dart | 78 ++--- lib/widgets/common_text_form_field.dart | 3 +- pubspec.lock | 20 +- pubspec.yaml | 1 + 21 files changed, 1185 insertions(+), 637 deletions(-) create mode 100644 lib/models/get_pd_response.dart create mode 100644 lib/provider/change_password_provider.dart create mode 100644 lib/provider/home_provider.dart diff --git a/lib/main.dart b/lib/main.dart index 25be7cb..f91a51a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:cheminova/provider/collect_kyc_provider.dart'; +import 'package:cheminova/provider/home_provider.dart'; import 'package:cheminova/screens/Attendance_success.dart'; import 'package:cheminova/screens/forgot_password_screen.dart'; import 'package:cheminova/screens/login_screen.dart'; @@ -10,9 +11,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { - runApp(MultiProvider(providers: [ - ChangeNotifierProvider(create: (context) => CollectKycProvider()) - ], child: const MyApp())); + runApp(MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => HomeProvider()), + ], + child: const MyApp())); } class MyApp extends StatelessWidget { diff --git a/lib/models/get_pd_response.dart b/lib/models/get_pd_response.dart new file mode 100644 index 0000000..553754b --- /dev/null +++ b/lib/models/get_pd_response.dart @@ -0,0 +1,74 @@ +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/attendance_provider.dart b/lib/provider/attendance_provider.dart index bea137b..388e21d 100644 --- a/lib/provider/attendance_provider.dart +++ b/lib/provider/attendance_provider.dart @@ -34,8 +34,19 @@ class AttendanceProvider extends ChangeNotifier { } else { return (false, response.data['message'].toString()); } - } catch (e) { + }on DioException catch (e) { setLoading(false); + if (e.response != null && e.response?.data != null) { + // Extracting the error message from the Dio response + return (false, e.response!.data['message'].toString()); + } else { + // When no response or response data is available + return (false, 'Something went wrong'); + } + } + catch (e) { + setLoading(false); + final message = e as DioException; return (false, 'Something want wrong'); } } diff --git a/lib/provider/change_password_provider.dart b/lib/provider/change_password_provider.dart new file mode 100644 index 0000000..353b301 --- /dev/null +++ b/lib/provider/change_password_provider.dart @@ -0,0 +1,51 @@ +import 'package:cheminova/services/api_client.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; + +import '../services/api_urls.dart'; + +class ChangePasswordProvider extends ChangeNotifier { + bool _isLoading = false; + + final oldPasswordController = TextEditingController(); + final newPasswordController = TextEditingController(); + final confirmPasswordController = TextEditingController(); + + bool get isLoading => _isLoading; + final _apiClient = ApiClient(); + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + Future<(bool, String)> changePassword() async { + setLoading(true); + try { + Response response = await _apiClient.put(ApiUrls.changePasswordUrl, + data: {'oldPassword': oldPasswordController.text.trim(), + 'confirmPassword': confirmPasswordController.text.trim(), + 'newPassword': newPasswordController.text.trim()}); + setLoading(false); + if (response.statusCode == 200) { + return (true, response.data['message'].toString()); + } else { + return (false, response.data['message'].toString()); + } + } + on DioException catch (e) { + setLoading(false); + if (e.response != null && e.response?.data != null) { + // Extracting the error message from the Dio response + return (false, e.response!.data['message'].toString()); + } else { + // When no response or response data is available + return (false, 'Something went wrong'); + } + } + catch (e) { + setLoading(false); + return (false, 'Something want wrong'); + } + } + +} \ No newline at end of file diff --git a/lib/provider/collect_kyc_provider.dart b/lib/provider/collect_kyc_provider.dart index 0837e80..bcf2bdd 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'))); + } + } + } } diff --git a/lib/provider/home_provider.dart b/lib/provider/home_provider.dart new file mode 100644 index 0000000..968cbe6 --- /dev/null +++ b/lib/provider/home_provider.dart @@ -0,0 +1,33 @@ +import 'package:cheminova/models/profile_response.dart'; +import 'package:cheminova/services/api_client.dart'; +import 'package:cheminova/services/api_urls.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; + +import '../models/get_pd_response.dart'; + +class HomeProvider extends ChangeNotifier { + final _apiClient = ApiClient(); + ProfileResponse? profileResponse; + bool _isLoading = false; + + bool get isLoading => _isLoading; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + Future getProfile() async { + setLoading(true); + try { + Response response = await _apiClient.get(ApiUrls.getProfileUrl); + setLoading(false); + if (response.statusCode == 200) { + profileResponse = ProfileResponse.fromJson(response.data); + } else {} + } catch (e) { + setLoading(false); + } + } +} diff --git a/lib/screens/change_password_screen.dart b/lib/screens/change_password_screen.dart index c5236ea..54e862b 100644 --- a/lib/screens/change_password_screen.dart +++ b/lib/screens/change_password_screen.dart @@ -1,3 +1,4 @@ +import 'package:cheminova/provider/change_password_provider.dart'; import 'package:cheminova/provider/login_provider.dart'; import 'package:cheminova/screens/password_change_screen.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; @@ -8,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../widgets/common_drawer.dart'; +import 'home_screen.dart'; class ChangePasswordPage extends StatefulWidget { const ChangePasswordPage({super.key}); @@ -17,132 +19,166 @@ class ChangePasswordPage extends StatefulWidget { } class _ChangePasswordPageState extends State { + late ChangePasswordProvider changePasswordProvider; final _formKey = GlobalKey(); + @override + void initState() { + changePasswordProvider = ChangePasswordProvider(); + super.initState(); + } + @override Widget build(BuildContext context) { - return CommonBackground( - isFullWidth: true, - child: Scaffold( - drawer: const CommonDrawer(), - - backgroundColor: Colors.transparent, - - appBar: CommonAppBar( - - title: const Text(''), - backgroundColor: Colors.transparent, - elevation: 0, - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - ), - body: SingleChildScrollView( - child: Column( - children: [ - // const SizedBox(height: 50), - // const SizedBox(height: 60), - Container( - padding: - const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 30.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), - child: Form( - key: _formKey, + return ChangeNotifierProvider( + create: (_) => changePasswordProvider, + builder: (context, child) { + return Stack( + children: [ + CommonBackground( + isFullWidth: true, + child: Scaffold( + drawer: const CommonDrawer(), + backgroundColor: Colors.transparent, + appBar: CommonAppBar( + title: const Text(''), + backgroundColor: Colors.transparent, + elevation: 0, + actions: [ + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + ), + body: SingleChildScrollView( child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const SizedBox(height: 20), - const Text('Change Password', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Roboto')), - const SizedBox(height: 20), - CommonTextFormField( - // controller: value.emailController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your email id'; - } - if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$') - .hasMatch(value)) { - return 'Please enter a valid email id'; - } - return null; - }, - title: 'Current Password'), - const SizedBox(height: 20), - CommonTextFormField( - // controller: value.passwordController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your password'; - } - return null; - }, - title: 'New Password'), const SizedBox(height: 20), - CommonTextFormField( - // controller: value.passwordController, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your password'; - } - return null; - }, - title: 'Confirm Password'), - const SizedBox(height: 15), - Align( - alignment: Alignment.center, - child: CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SIGN IN', - isLoading: false, - onPressed: () { - Navigator.push(context, MaterialPageRoute(builder:(context) => const PasswordChangeScreen())); - }, - // onPressed: value.isLoading - // ? null - // : () async { - // if (_formKey.currentState!.validate()) { - // value.login().then((result) { - // var (status, message) = result; - // ScaffoldMessenger.of(context) - // .showSnackBar(SnackBar( - // content: Text(message))); - // if (status) { - // Navigator.pushReplacement( - // context, - // MaterialPageRoute( - // builder: (context) => - // const HomePage())); - // } - // }); - // } - // }, - )) + children: [ + // const SizedBox(height: 50), + // const SizedBox(height: 60), + Container( + padding: const EdgeInsets.all(20.0) + .copyWith(top: 15, bottom: 30), + margin: const EdgeInsets.symmetric(horizontal: 30.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.9), + borderRadius: BorderRadius.circular(26.0)), + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 20), + const Text('Change Password', + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w500, + fontFamily: 'Roboto')), + const SizedBox(height: 20), + Consumer( + builder: (context, value, child) => + CommonTextFormField( + controller: value.oldPasswordController, + validator: (val) { + if (val == null || val.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + title: 'Old Password'), + ), + const SizedBox(height: 20), + Consumer( + builder: (context, value, child) => + CommonTextFormField( + controller: value.newPasswordController, + validator: (val) { + if (val == null || val.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + title: 'New Password'), + ), + const SizedBox(height: 20), + Consumer( + builder: (context, value, child) => + CommonTextFormField( + controller: + value.confirmPasswordController, + validator: (val) { + if (val == null || val.isEmpty) { + return 'Please enter your password'; + } else if (val != + value + .newPasswordController.text) { + return 'Password does not match'; + } + return null; + }, + title: 'Confirm Password'), + ), + const SizedBox(height: 15), + Align( + alignment: Alignment.center, + child: Consumer( + builder: (context, value, child) => + CommonElevatedButton( + backgroundColor: const Color(0xff004791), + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SIGN IN', + isLoading: false, + onPressed: value.isLoading + ? null + : () async { + if (_formKey.currentState! + .validate()) { + value + .changePassword() + .then((result) { + var (status, message) = + result; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: + Text(message))); + if (status) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const HomePage())); + } + }); + } + }, + ), + )) + ], + ), + ), + ), ], ), - ), - ), - ], + )), ), - )), - ); + Consumer( + builder: (context, value, child) => value.isLoading? + Container( + color: Colors.black.withOpacity(0.5), + child: const Center(child: CircularProgressIndicator())):Container(), + ) + ], + ); + }); } } 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/forgot_password_screen.dart b/lib/screens/forgot_password_screen.dart index a857dc9..1146b4d 100644 --- a/lib/screens/forgot_password_screen.dart +++ b/lib/screens/forgot_password_screen.dart @@ -1,8 +1,12 @@ +import 'package:cheminova/provider/change_password_provider.dart'; import 'package:cheminova/screens/verify_phone_screen.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:cheminova/widgets/common_text_form_field.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'home_screen.dart'; class ForgotPasswordScreen extends StatefulWidget { const ForgotPasswordScreen({super.key}); @@ -12,6 +16,8 @@ class ForgotPasswordScreen extends StatefulWidget { } class _ForgotPasswordScreenState extends State { + final _formKey = GlobalKey(); + @override Widget build(BuildContext context) { return CommonBackground( @@ -31,74 +37,98 @@ class _ForgotPasswordScreenState extends State { color: const Color(0xffB4D1E5).withOpacity(0.9), borderRadius: BorderRadius.circular(26.0), ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Align( - alignment: Alignment.topLeft, - child: Image.asset( - 'assets/lock_logo2.png', - height: 50.0, // Adjust the height as needed - width: 50.0, // Adjust the width as needed + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Align( + alignment: Alignment.topLeft, + child: Image.asset( + 'assets/lock_logo2.png', + height: 50.0, // Adjust the height as needed + width: 50.0, // Adjust the width as needed + ), + ), + const Text( + 'Forgot Password', + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w500, + fontFamily: 'Anek', ), - ), - const Text( - 'Forgot Password', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Anek', ), - ), - const Text( - 'Enter Registered Email ID to generate new password', - style: TextStyle( - fontSize: 14, - color: Colors.black, - fontWeight: FontWeight.w300, - fontFamily: 'Roboto', + const Text( + 'Enter Registered Email ID to generate new password', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.w300, + fontFamily: 'Roboto', + ), ), - ), - const SizedBox(height: 20), - const CommonTextFormField(title: ' Enter Your Email ID'), - const SizedBox(height: 20), - Align( - alignment: Alignment.center, - child: TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Back to Login', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Roboto')), + const SizedBox(height: 20), + const CommonTextFormField(title: ' Enter Your Email ID'), + const SizedBox(height: 20), + Align( + alignment: Alignment.center, + child: TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Back to Login', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Roboto')), + ), ), - ), - const SizedBox(height: 15), - Align( - alignment: Alignment.center, - child: CommonElevatedButton( - backgroundColor: const Color(0xff004791), - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SEND', - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const VerifyPhoneScreen(), - ), - ); - }, + const SizedBox(height: 15), + Align( + alignment: Alignment.center, + child: Consumer( + builder: (context, value, child) => CommonElevatedButton( + backgroundColor: const Color(0xff004791), + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SEND', + onPressed: value.isLoading + ? null + : () async { + if (_formKey.currentState!.validate()) { + value.changePassword().then((result) { + var (status, message) = result; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: Text(message))); + if (status) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const HomePage())); + } + }); + } + }, + // onPressed: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => const VerifyPhoneScreen(), + // ), + // ); + // }, + ), + ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 3bf3ec0..7817a04 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,49 +1,65 @@ +import 'package:cheminova/provider/home_provider.dart'; import 'package:cheminova/screens/calendar_screen.dart'; import 'package:cheminova/screens/collect_kyc_screen.dart'; +import 'package:cheminova/screens/daily_tasks_screen.dart'; +import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:cheminova/screens/mark_attendence_screen.dart'; import 'package:cheminova/screens/notification_screen.dart'; +import 'package:cheminova/screens/product_sales_data.dart'; import 'package:cheminova/screens/products_manual_screen.dart'; import 'package:cheminova/screens/summary_screen.dart'; -import 'package:cheminova/screens/product_sales_data.dart'; import 'package:cheminova/screens/update_inventory_screen.dart'; -import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; +import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:cheminova/screens/daily_tasks_screen.dart'; +import 'package:provider/provider.dart'; -class HomePage extends StatelessWidget { +class HomePage extends StatefulWidget { const HomePage({super.key}); + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + @override + void initState() { + Provider.of(context, listen: false).getProfile(); + super.initState(); + } + @override Widget build(BuildContext context) { return CommonBackground( child: Scaffold( backgroundColor: Colors.transparent, - appBar: const CommonAppBar( - title: Row( - children: [ - // CircleAvatar( - // backgroundImage: AssetImage( - // 'assets/profile.png'), // Replace with actual user image - // ), - SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Welcome', - style: TextStyle( - color: Colors.black87, - fontSize: 14, - fontWeight: FontWeight.w400)), - Text('User Name', - style: TextStyle( - color: Colors.black87, fontSize: 20)), - ], - ), - ], - ), backgroundColor: Colors.transparent, elevation: 0, + appBar: CommonAppBar( + title: Row(children: [ + // CircleAvatar( + // backgroundImage: AssetImage( + // 'assets/profile.png'), // Replace with actual user image + // ), + const SizedBox(width: 10), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text('Welcome', + style: TextStyle( + color: Colors.black87, + fontSize: 14, + fontWeight: FontWeight.w400)), + Consumer( + builder: (context, value, child) => + (value.profileResponse == null || + value.profileResponse!.myData == null || + value.profileResponse!.myData!.name == null) + ? const SizedBox() + : Text(value.profileResponse!.myData!.name ?? '', + style: const TextStyle( + color: Colors.black87, fontSize: 20))) + ]) + ]), + backgroundColor: Colors.transparent, + elevation: 0, ), drawer: const CommonDrawer(), body: SafeArea( @@ -62,7 +78,8 @@ class HomePage extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => const MarkAttendanceScreen(), + builder: (context) => + const MarkAttendanceScreen(), )); }, ), @@ -82,14 +99,16 @@ class HomePage extends StatelessWidget { Row( children: [ Expanded( - child: _buildCustomCard('Display\nSales data', - 'Quickly display Sales', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const DisplaySalesScreen(), - )); - }), + child: _buildCustomCard( + 'Display\nSales data', 'Quickly display Sales', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const DisplaySalesScreen(), + )); + }), ), const SizedBox( width: 12, @@ -97,12 +116,13 @@ class HomePage extends StatelessWidget { Expanded( child: _buildCustomCard('Update Inventory Data', 'Quickly Inventory Data', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const UpdateInventoryScreen(), - )); - }), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const UpdateInventoryScreen(), + )); + }), ), ], ), @@ -112,7 +132,8 @@ class HomePage extends StatelessWidget { Row( children: [ Expanded( - child: _buildCustomCard('Summary', '\n\n', onTap: () { + child: + _buildCustomCard('Summary', '\n\n', onTap: () { Navigator.push( context, MaterialPageRoute( @@ -125,11 +146,13 @@ class HomePage extends StatelessWidget { ), Expanded( child: _buildCustomCard( - 'Product\nSales Data Visibility', '', onTap: () { + 'Product\nSales Data Visibility', '', + onTap: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => const ProductSalesData(), + builder: (context) => + const ProductSalesData(), )); }), ), @@ -143,12 +166,13 @@ class HomePage extends StatelessWidget { Expanded( child: _buildCustomCard('Collect \nKYC Data', 'Scan and upload KYC Documents', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const CollectKycScreen(), - )); - }), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const CollectKycScreen(), + )); + }), ), ], ), @@ -156,23 +180,33 @@ class HomePage extends StatelessWidget { Row( children: [ Expanded( - child: _buildCustomCard('Notifications', - 'Tasks & Alerts\n\n', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const NotificationScreen(), - )); - }), + child: _buildCustomCard( + 'Notifications', 'Tasks & Alerts\n\n', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const NotificationScreen(), + )); + }), ), const SizedBox( width: 15, ), Expanded( - child: _buildCustomCard('Calendar', - ' Upcoming Appointments & Deadlines',onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const CalendarScreen(),)); - },), + child: _buildCustomCard( + 'Calendar', + ' Upcoming Appointments & Deadlines', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const CalendarScreen(), + )); + }, + ), ), ], ), @@ -180,14 +214,16 @@ class HomePage extends StatelessWidget { Row( children: [ Expanded( - child: _buildCustomCard('Products Manual', - 'details of products', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ProductsManualScreen(), - )); - }), + child: _buildCustomCard( + 'Products Manual', 'details of products', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const ProductsManualScreen(), + )); + }), ), ], ), @@ -202,7 +238,8 @@ class HomePage extends StatelessWidget { ); } - Widget _buildCustomCard(String title, String subtitle, {void Function()? onTap}) { + Widget _buildCustomCard(String title, String subtitle, + {void Function()? onTap}) { return Container( margin: const EdgeInsets.only(bottom: 10), decoration: BoxDecoration( @@ -222,9 +259,9 @@ class HomePage extends StatelessWidget { ), subtitle: subtitle.isNotEmpty ? Text( - subtitle, - style: const TextStyle(color: Colors.white70, fontSize: 13), - ) + subtitle, + style: const TextStyle(color: Colors.white70, fontSize: 13), + ) : null, onTap: onTap, ), diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 6755470..90619c3 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -7,6 +7,8 @@ import 'package:cheminova/widgets/common_text_form_field.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'home_screen.dart'; + class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -139,28 +141,28 @@ class _LoginPageState extends State { height: kToolbarHeight - 10, text: 'SIGN IN', isLoading: value.isLoading, - onPressed: () { - Navigator.push(context, MaterialPageRoute(builder:(context) => const VerifyPhoneScreen())); - }, - // onPressed: value.isLoading - // ? null - // : () async { - // if (_formKey.currentState!.validate()) { - // value.login().then((result) { - // var (status, message) = result; - // ScaffoldMessenger.of(context) - // .showSnackBar(SnackBar( - // content: Text(message))); - // if (status) { - // Navigator.pushReplacement( - // context, - // MaterialPageRoute( - // builder: (context) => - // const HomePage())); - // } - // }); - // } - // }, + // onPressed: () { + // Navigator.push(context, MaterialPageRoute(builder:(context) => const VerifyPhoneScreen())); + // }, + onPressed: value.isLoading + ? null + : () async { + if (_formKey.currentState!.validate()) { + value.login().then((result) { + var (status, message) = result; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar( + content: Text(message))); + if (status) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + const HomePage())); + } + }); + } + }, ), )) ], diff --git a/lib/screens/retailer_details_screen.dart b/lib/screens/retailer_details_screen.dart index fa9a3af..2bf6144 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,26 @@ 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 +108,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 +123,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 +138,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 +153,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 +165,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 +178,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 +195,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 +211,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 +228,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({ + Key? key, + required this.country, + required this.state, + required this.city, + required this.dialogColor, + required this.textFieldDecoration, + }) : super(key: key); + + @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/splash_screen.dart b/lib/screens/splash_screen.dart index ed7e014..4e9f801 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -1,7 +1,10 @@ import 'package:cheminova/constants/constant.dart'; import 'package:cheminova/screens/login_screen.dart'; +import 'package:cheminova/services/secure__storage_service.dart'; import 'package:flutter/material.dart'; +import 'home_screen.dart'; + class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @@ -14,8 +17,16 @@ class _SplashScreenState extends State { void initState() { super.initState(); Future.delayed(const Duration(seconds: 3), () { - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => const LoginPage())); + SecureStorageService().read( + key: 'access_token').then((value){ + if(value != null){ + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => const HomePage())); + }else{ + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => const LoginPage())); + } + }); }); } diff --git a/lib/screens/upload_documents_screen.dart b/lib/screens/upload_documents_screen.dart index 63ad85b..810f477 100644 --- a/lib/screens/upload_documents_screen.dart +++ b/lib/screens/upload_documents_screen.dart @@ -17,74 +17,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 +26,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 +70,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 +80,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 +126,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..436cacf 100644 --- a/lib/screens/verification_documents_screen.dart +++ b/lib/screens/verification_documents_screen.dart @@ -1,9 +1,9 @@ import 'package:cheminova/provider/collect_kyc_provider.dart'; -import 'package:flutter/material.dart'; +import 'package:cheminova/screens/data_submit_successfull.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 +22,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 +50,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 +83,21 @@ 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 +108,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 +126,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 +143,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 +164,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 65ec84d..22a856c 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -29,11 +29,11 @@ class ApiClient { })); } - 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 386d2fd..cd21c84 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -1,7 +1,10 @@ class ApiUrls { - static const String baseUrl = - 'https://cheminova-api-2.onrender.com/api/'; + static const String baseUrl = 'https://cheminova-api-2.onrender.com/api/'; static const String loginUrl = 'salescoordinator/login'; static const String profileUrl = 'my-profile'; static const String markAttendanceUrl = 'v1/markattendance/salescoordinator'; -} \ No newline at end of file + static const String getPdUrl = 'kyc/get-pd'; + static const String createCollectKycUrl = '${baseUrl}kyc/create'; + static const String getProfileUrl = '${baseUrl}salescoordinator/my-profile'; + static const String changePasswordUrl = '${baseUrl}salescoordinator/password/update'; +} diff --git a/lib/widgets/common_drawer.dart b/lib/widgets/common_drawer.dart index b22713a..7a1b098 100644 --- a/lib/widgets/common_drawer.dart +++ b/lib/widgets/common_drawer.dart @@ -1,7 +1,9 @@ +import 'package:cheminova/provider/home_provider.dart'; import 'package:cheminova/screens/change_password_screen.dart'; import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/screens/login_screen.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class CommonDrawer extends StatelessWidget { const CommonDrawer({super.key}); @@ -12,50 +14,36 @@ class CommonDrawer extends StatelessWidget { child: ListView( padding: EdgeInsets.zero, children: [ - Container( - height: 150, - child: const DrawerHeader( - decoration: BoxDecoration( - color: Colors.black87, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start,mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'Username', - style: TextStyle( - color: Colors.white, - fontSize: 18, - ), - ),Text( - 'Employee ID', - style: TextStyle( - color: Colors.white, - fontSize: 20, - ), + SizedBox( + height: 150, + child: DrawerHeader( + decoration: const BoxDecoration( + color: Colors.black87, ), - ], - ), - ), - ), - // const UserAccountsDrawerHeader( - // accountName: Text("Name"), - // accountEmail: Text("Employee ID"), - // // currentAccountPicture: CircleAvatar( - // // backgroundImage: AssetImage( - // // 'assets/profile.png'), // Replace with actual user image - // // ), - // decoration: BoxDecoration( - // color: Colors.black87, - // ), - // ), + child: Consumer( + builder: (context, value, child) => + (value.profileResponse == null || + value.profileResponse!.myData == null || + value.profileResponse!.myData!.name == null) + ? const SizedBox() + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text(value.profileResponse!.myData!.name!, + style: const TextStyle( + color: Colors.white, + fontSize: 18)), + Text(value.profileResponse!.myData!.sId!, + style: const TextStyle( + color: Colors.white, + fontSize: 15)) + ])))), ListTile( - leading: const Icon(Icons.home), - title: const Text('Home'), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const HomePage(),)); - }, - ), + leading: const Icon(Icons.home), + title: const Text('Home'), + onTap: () => Navigator.push(context, + MaterialPageRoute(builder: (context) => const HomePage()))), ListTile( leading: const Icon(Icons.account_circle), title: const Text('Profile'), @@ -67,7 +55,11 @@ class CommonDrawer extends StatelessWidget { leading: const Icon(Icons.settings), title: const Text('Change Password'), onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const ChangePasswordPage(),)); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ChangePasswordPage(), + )); }, ), ListTile( diff --git a/lib/widgets/common_text_form_field.dart b/lib/widgets/common_text_form_field.dart index bb457f1..14d320d 100644 --- a/lib/widgets/common_text_form_field.dart +++ b/lib/widgets/common_text_form_field.dart @@ -39,13 +39,14 @@ const CommonTextFormField( TextFormField( controller: controller, readOnly: readOnly ?? false, - maxLines: maxLines, + maxLines: maxLines??1, maxLength: maxLength, onTapOutside: (event) => FocusScope.of(context).unfocus(), validator: validator, keyboardType: keyboardType, inputFormatters: inputFormatters, decoration: InputDecoration( + counterText: "", hintText: title, contentPadding: const EdgeInsets.only(bottom: 10, left: 10), filled: true, diff --git a/pubspec.lock b/pubspec.lock index 8dcdd7d..9e91a02 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: @@ -444,10 +452,10 @@ packages: dependency: transitive description: name: geolocator_android - sha256: "00c7177a95823dd3ee35ef42fd8666cd27d219ae14cea472ac76a21dff43000b" + sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47" url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.6.1" geolocator_apple: dependency: transitive description: @@ -540,10 +548,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: "8c3168469b005a6dbf5ba01f795917ae4f4e71077d3d7f2049a0d25a4760393e" + sha256: "3fe99e3a459b969f657565a853353bdea7e3d3cae34f7dd124836267d426ec87" url: "https://pub.dev" source: hosted - version: "0.8.12+8" + version: "0.8.12+10" image_picker_for_web: dependency: transitive description: @@ -748,10 +756,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e84c8a53fe1510ef4582f118c7b4bdf15b03002b51d7c2b66983c65843d61193 + sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" url: "https://pub.dev" source: hosted - version: "2.2.8" + version: "2.2.9" path_provider_foundation: dependency: transitive 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: