diff --git a/lib/provider/add_sales_provider.dart b/lib/provider/add_sales_provider.dart index 5f035f5..379a5f4 100644 --- a/lib/provider/add_sales_provider.dart +++ b/lib/provider/add_sales_provider.dart @@ -65,7 +65,6 @@ class AddSalesProvider with ChangeNotifier { "addedForId": pdRdId, "tradename": tradeName, "date": date, - "products": selectedProducts.map((product) { return { "SKU": product.SKU, @@ -75,7 +74,6 @@ class AddSalesProvider with ChangeNotifier { "SalesAmount": product.SalesAmount }; }).toList() - // "products": selectedProducts.map((e) => e.toJson()).toList() })); setLoading(false); if (response.statusCode == 201) { @@ -84,33 +82,14 @@ class AddSalesProvider with ChangeNotifier { ).showSnackBar( SnackBar(content: Text(response.data['message'])), ); - if (inventoryId != null) { - _apiClient - .put(ApiUrls.updateTaskInventoryUrl + inventoryId, data: null) - .then((value) { - debugPrint('Task Updated'); - if (value.statusCode == 200) { - resetProducts(); - Navigator.push( - navigatorKey.currentContext!, - MaterialPageRoute( - builder: (context) => - const DataSubmitSuccessfull())); - } else { - ScaffoldMessenger.of( - navigatorKey.currentContext!, - ).showSnackBar( - const SnackBar(content: Text('Task not updated')), - ); - } - }); - } else { - resetProducts(); - Navigator.push( - navigatorKey.currentContext!, - MaterialPageRoute( - builder: (context) => const DataSubmitSuccessfull())); - } + + resetProducts(); + Navigator.push( + navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) => const DataSubmitSuccessfull(), + ), + ); } } catch (e) { setLoading(false); @@ -123,4 +102,4 @@ class AddSalesProvider with ChangeNotifier { tasksList.clear(); notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/provider/product_provider.dart b/lib/provider/product_provider.dart index 2e806f7..b8bf3ad 100644 --- a/lib/provider/product_provider.dart +++ b/lib/provider/product_provider.dart @@ -52,6 +52,7 @@ class ProductProvider extends ChangeNotifier { "ProductName": product.name, "Sale": product.sale, "Inventory": product.inventory, + "productid": product.id, }; }).toList(), "addedFor": addedFor, diff --git a/lib/provider/task_provider.dart b/lib/provider/task_provider.dart index d34602c..3523e1a 100644 --- a/lib/provider/task_provider.dart +++ b/lib/provider/task_provider.dart @@ -245,4 +245,24 @@ class TaskProvider extends ChangeNotifier { setLoading(false); } } + + Future getAllTaskByDate(DateTime date) async { + clear(); + setLoading(true); + try { + final String formatedDate = DateFormat('dd/MM/yyyy').format(date); + Response response = + await _apiClient.get("${ApiUrls.allTaskByDate}?Date=$formatedDate"); + if (response.statusCode == 200) { + List data = (response.data['tasks'] as List) + .map((json) => TaskModel.fromJson(json)) + .toList(); + _taskModelList = data; + } + } catch (e) { + print("Error occurred: $e"); + } finally { + setLoading(false); + } + } } diff --git a/lib/provider/visit_pdrd_provider.dart b/lib/provider/visit_pdrd_provider.dart index 28159b2..e7532f0 100644 --- a/lib/provider/visit_pdrd_provider.dart +++ b/lib/provider/visit_pdrd_provider.dart @@ -1,32 +1,52 @@ -import 'package:cheminova/screens/data_submit_successfull.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import '../constants/constant.dart'; +import '../screens/data_submit_successfull.dart'; import '../services/api_client.dart'; import '../services/api_urls.dart'; class VisitPdRdProvider with ChangeNotifier { final _apiClient = ApiClient(); bool _isLoading = false; - - bool get isLoading => _isLoading; - void setLoading(bool loading) { _isLoading = loading; notifyListeners(); } - Future submitVisitPdRd(BuildContext context, String id) async { + bool get isLoading => _isLoading; + + Future submitVisitPdRd({ + required String pdRdId, + required String distributorType, + required String tradeName, + required String visitDate, + required String visitTime, + required String visitPurpose, + required String meetingSummary, + String? followUp, + String? nextVisitDate, + }) async { setLoading(true); try { - Response response = - await _apiClient.put(ApiUrls.updateTaskInventoryUrl + id); - debugPrint('Response: $response'); + Response response = await _apiClient.post(ApiUrls.visitRdPd, data: { + "addedFor": distributorType, + "addedForId": pdRdId, + "tradename": tradeName, + "visitDate": visitDate, + "visitTime": visitTime, + "visitpurpose": visitPurpose, + "meetingsummary": meetingSummary, + if (followUp != null) "followup": followUp, + if (nextVisitDate != null) "nextvisitdate": nextVisitDate + }); setLoading(false); - if (response.statusCode == 200) { + if (response.statusCode == 201) { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const DataSubmitSuccessfull())); + navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) => const DataSubmitSuccessfull(), + ), + ); } } catch (e) { setLoading(false); diff --git a/lib/screens/Visit_Dealers_screen.dart b/lib/screens/Visit_Dealers_screen.dart deleted file mode 100644 index 33a41e4..0000000 --- a/lib/screens/Visit_Dealers_screen.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:cheminova/provider/visit_pdrd_provider.dart'; -import 'package:cheminova/widgets/common_drawer.dart'; -import 'package:flutter/material.dart'; -import 'package:cheminova/widgets/common_background.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; -import '../widgets/common_app_bar.dart'; -import '../widgets/common_elevated_button.dart'; -import '../widgets/common_text_form_field.dart'; -import 'package:image_picker/image_picker.dart'; - -class VisitDealersScreen extends StatefulWidget { - final String? tradeName; - final String? id; - const VisitDealersScreen({super.key, this.tradeName, this.id}); - - @override - State createState() => VisitDealersScreenState(); -} - -class VisitDealersScreenState extends State { - late VisitPdRdProvider _visitPdRdProvider; - final dateController = TextEditingController( - text: DateFormat('dd/MM/yyyy').format(DateTime.now())); - - final timeController = - TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); - - final notesController = TextEditingController(); - final dealerController = TextEditingController(); - final meetingSummaryController = TextEditingController(); - final followUpActionsController = TextEditingController(); - final nextVisitDateController = TextEditingController(); - late TextEditingController retailerController = TextEditingController(); - - String selectedPurpose = 'Sales'; - List purposeOptions = ['Sales', 'Dues collection', 'Others']; - - Future _pickImage() async { - final ImagePicker picker = ImagePicker(); - await picker.pickImage(source: ImageSource.camera); - // Handle the picked image - } - - @override - void initState() { - _visitPdRdProvider = VisitPdRdProvider(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - retailerController = TextEditingController(text: widget.tradeName); - - return ChangeNotifierProvider( - create: (context) => _visitPdRdProvider, - child: CommonBackground( - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: CommonAppBar( - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - title: Column( - children: [ - const Text('Visit Retailers', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - Text(widget.tradeName ?? '', - style: const TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), - ], - ), - backgroundColor: Colors.transparent, - elevation: 0, - ), - drawer: const CommonDrawer(), - body: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 16), - Container( - padding: - const EdgeInsets.all(20.0).copyWith(top: 30, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 16.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CommonTextFormField( - readOnly: true, - title: 'Select Retailer', - fillColor: Colors.white, - controller: retailerController), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Visit date', - readOnly: true, - fillColor: Colors.white, - controller: dateController), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Time', - readOnly: true, - fillColor: Colors.white, - controller: timeController), - const SizedBox(height: 15), - DropdownButtonFormField( - decoration: const InputDecoration( - labelText: 'Purpose of visit', - fillColor: Colors.white, - filled: true, - ), - value: selectedPurpose, - items: purposeOptions.map((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - onChanged: (String? newValue) { - setState(() { - selectedPurpose = newValue!; - }); - }, - ), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Meeting Summary:', - fillColor: Colors.white, - maxLines: 4, - controller: meetingSummaryController), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Follow-up Actions:', - fillColor: Colors.white, - maxLines: 4, - controller: followUpActionsController), - const SizedBox(height: 15), - CommonTextFormField( - title: 'Next visit date:', - readOnly: true, - fillColor: Colors.white, - controller: nextVisitDateController), - const SizedBox(height: 15), - Row( - children: [ - Expanded( - child: CommonTextFormField( - title: 'Attach Documents/Photos', - fillColor: Colors.white, - controller: notesController), - ), - IconButton( - icon: const Icon(Icons.camera_alt), - onPressed: _pickImage, - ), - ], - ), - const SizedBox(height: 15), - Consumer( - builder: (context, value, child) => Align( - alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SUBMIT', - backgroundColor: const Color(0xff004791), - onPressed: () { - value.submitVisitPdRd(context, widget.id ?? ''); - }, - ), - ), - ), - ], - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/screens/add_products_screen.dart b/lib/screens/add_products_screen.dart index e2ab036..30b9a89 100644 --- a/lib/screens/add_products_screen.dart +++ b/lib/screens/add_products_screen.dart @@ -161,7 +161,8 @@ class _AddProductsScreenState extends State { child: ListTile( title: Text( filteredProducts[index] - .name.capitalize(), + .name + .capitalize(), style: TextStyle( color: isAlreadySelected ? Colors.grey @@ -222,7 +223,10 @@ class _AddProductsScreenState extends State { onPressed: () { provider .submitSelectedProducts( - "PrincipalDistributor", + (widget.distributorType == + 'Principal Distributor') + ? 'PrincipalDistributor' + : 'RetailDistributor', widget.distributor.id!) .then((value) { if (value) { @@ -233,6 +237,12 @@ class _AddProductsScreenState extends State { const DataSubmitSuccessfull(), ), ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Insufficient stock"), + ), + ); } }); }, @@ -278,15 +288,8 @@ class _ProductBlockState extends State { inventoryController.text.isNotEmpty) { // Check if both inputs are valid integers try { - int sale = int.parse(saleController.text); - int inventory = int.parse(inventoryController.text); - - // Validation: inventory should be less than or equal to sales - if (inventory > sale) { - errorMessage = 'Inventory should be less than or equal to sales'; - } else { - errorMessage = null; - } + int.parse(saleController.text); + int.parse(inventoryController.text); } catch (e) { // Handle the case where input is not a valid integer errorMessage = 'Please enter valid integer values for both fields'; @@ -309,7 +312,8 @@ class _ProductBlockState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Product: ${provider.selectedProducts[widget.index].name.capitalize()}', + Text( + 'Product: ${provider.selectedProducts[widget.index].name.capitalize()}', style: const TextStyle(fontSize: 16)), Text('SKU: ${provider.selectedProducts[widget.index].SKU}', style: const TextStyle(fontSize: 15)), diff --git a/lib/screens/assign_tasks_screen.dart b/lib/screens/assign_tasks_screen.dart index 17eb00e..bd82f9a 100644 --- a/lib/screens/assign_tasks_screen.dart +++ b/lib/screens/assign_tasks_screen.dart @@ -394,7 +394,15 @@ class _AssignTasksScreenState extends State { .map((PdRdResponseModel distributor) { return DropdownMenuItem( value: distributor, - child: Text(distributor.name!.capitalize()), + child: Text( + (selectedDistributorType == + 'PrincipalDistributor' + ? distributor + .shippingAddress!.tradeName + .capitalize() + : distributor.kyc!.tradeName + .capitalize()), + ), ); }).toList(), onChanged: (value) { diff --git a/lib/screens/calendar_screen.dart b/lib/screens/calendar_screen.dart index 71d9cbf..20c5c58 100644 --- a/lib/screens/calendar_screen.dart +++ b/lib/screens/calendar_screen.dart @@ -1,44 +1,61 @@ +import 'package:cheminova/models/task_model.dart'; +import 'package:cheminova/provider/task_provider.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; import '../widgets/common_app_bar.dart'; import '../widgets/common_background.dart'; import '../widgets/common_drawer.dart'; - -DateTime _focusedDay=DateTime.now(); -DateTime? _selectedDay=DateTime.now(); +DateTime _focusedDay = DateTime.now(); +DateTime _selectedDay = DateTime.now(); class CalendarScreen extends StatelessWidget { const CalendarScreen({super.key}); - @override Widget build(BuildContext context) { - return CommonBackground( + final taskProvider = context.watch(); + + return CommonBackground( child: Scaffold( appBar: CommonAppBar( actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], title: const Text('Calendar'), backgroundColor: Colors.transparent, elevation: 0, ), backgroundColor: Colors.transparent, drawer: const CommonDrawer(), - body: const SingleChildScrollView( - child: Column( - children: [ - CalendarWidget(), - EventsList(), - ], - ), + body: Stack( + children: [ + const Column( + children: [ + CalendarWidget(), + TaskList(), + ], + ), + if (taskProvider.isLoading) + Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + ), + child: const Center( + child: CircularProgressIndicator(), + ), + ), + ], ), ), ); @@ -59,110 +76,137 @@ class _CalendarWidgetState extends State { margin: const EdgeInsets.all(16), child: Padding( padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Calendar', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const Text( - 'Month/Year', - style: TextStyle(fontSize: 14, color: Colors.grey), - ), - const SizedBox(height: 10), - TableCalendar( - firstDay: DateTime.utc(1900, 5, 1), - lastDay: DateTime.utc(2900, 5, 1), - focusedDay: _focusedDay, - selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); - }, - onDaySelected: (selectedDay, focusedDay) { - setState(() { - _selectedDay = selectedDay; - _focusedDay = focusedDay; - }); - }, - onPageChanged: (focusedDay) { - _focusedDay = focusedDay; - }, - ), - ], + child: TableCalendar( + firstDay: DateTime.utc(1900, 5, 1), + lastDay: DateTime.utc(2900, 5, 1), + focusedDay: _focusedDay, + selectedDayPredicate: (day) { + return isSameDay(_selectedDay, day); + }, + onDaySelected: (selectedDay, focusedDay) { + setState(() { + _selectedDay = selectedDay; + _focusedDay = focusedDay; + context.read().getAllTaskByDate(_selectedDay); + }); + }, + onPageChanged: (focusedDay) { + _focusedDay = focusedDay; + }, ), ), ); } } -class EventsList extends StatelessWidget { - const EventsList({super.key}); +class TaskList extends StatefulWidget { + const TaskList({super.key}); + + @override + State createState() => _TaskListState(); +} + +class _TaskListState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().getAllTaskByDate(_selectedDay); + }); + } @override Widget build(BuildContext context) { - return const Card( - margin: EdgeInsets.all(16), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Events List', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + final taskProvider = context.watch(); + + return (taskProvider.taskModelList.isEmpty) + ? Container( + alignment: Alignment.center, + child: const Text('No task found', + style: TextStyle(fontSize: 20, color: Colors.white)), + ) + : Expanded( + child: ListView.builder( + itemCount: taskProvider.taskModelList.length, + itemBuilder: (context, index) { + return _taskView(task: taskProvider.taskModelList[index]); + }, ), - SizedBox(height: 10), - EventItem( - date: '10-06-2024', - event: 'Meeting with Territory Manager', - ), - SizedBox(height: 10), - EventItem( - date: '10-06-2024', - event: 'Sales Data Review', - ), - ], + ); + } + + Widget _customContainer({required Widget child}) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.8), + borderRadius: BorderRadius.circular(16.0), ), + child: child, ), ); } + + Widget _taskView({required TaskModel task}) { + final formattedDate = DateFormat('dd/MM/yyyy').format(task.taskDueDate); + return Column( + children: [ + _customContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Assigned to: ${task.taskAssignedTo.name}", + style: const TextStyle( + fontFamily: 'Anek', + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5), + Text( + "Task: ${task.task}", + style: const TextStyle( + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 5), + if (task.addedFor != null) ...{ + task.addedFor == 'PrincipalDistributor' + ? Text("PD: ${task.tradename}") + : Text("RD: ${task.tradename}"), + const SizedBox(height: 5), + }, + Text( + 'Status: ${task.taskStatus}', + style: const TextStyle( + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 5), + if (task.note != null) ...{ + Text( + 'Note: ${task.note}', + style: const TextStyle( + fontFamily: 'Anek', + ), + ), + const SizedBox(height: 5), + }, + Text( + 'Deadline: $formattedDate', + style: const TextStyle( + fontFamily: 'Anek', + ), + ), + ], + ), + ), + const SizedBox(height: 10), + ], + ); + } } - -class EventItem extends StatelessWidget { - final String date; - final String event; - - const EventItem({super.key, required this.date, required this.event}); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.blue[50], - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Date: $date'), - Text( - 'Event: $event', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 8), - ElevatedButton( - onPressed: () { - // Add view details functionality here - }, - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue[800], - ), - child: const Text('VIEW DETAILS'), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/screens/daily_tasks_screen.dart b/lib/screens/daily_tasks_screen.dart index b2218de..13078b9 100644 --- a/lib/screens/daily_tasks_screen.dart +++ b/lib/screens/daily_tasks_screen.dart @@ -1,8 +1,8 @@ import 'package:cheminova/screens/collect_kyc_screen.dart'; import 'package:cheminova/screens/select_distributer_screen.dart'; import 'package:cheminova/screens/display_sales_screen.dart'; +import 'package:cheminova/screens/visit_rd_pd_screen.dart'; import 'package:flutter/material.dart'; -import 'package:cheminova/screens/visit_Dealers_screen.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_background.dart'; @@ -48,12 +48,14 @@ class DailyTasksScreen extends StatelessWidget { 'Visit Dealers/Retailers', '', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const VisitDealersScreen(), - ), - ); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => const VisitRdPdScreen( + // tradeName: 't', + // ), + // ), + // ); }, ), const SizedBox(height: 5), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index ad4e7f8..62ca66e 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,6 +1,6 @@ import 'package:cheminova/notification_service.dart'; import 'package:cheminova/provider/user_provider.dart'; -import 'package:cheminova/screens/Visit_Dealers_screen.dart'; +import 'package:cheminova/screens/visit_rd_pd_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'; @@ -160,7 +160,9 @@ class _HomePageState extends State { context, MaterialPageRoute( builder: (context) => - const VisitDealersScreen(), + const SelectDistributerScreen( + task: "Visit RD/PD", + ), ), ); }, diff --git a/lib/screens/retailer_details_screen.dart b/lib/screens/retailer_details_screen.dart index e55b1e7..703aa88 100644 --- a/lib/screens/retailer_details_screen.dart +++ b/lib/screens/retailer_details_screen.dart @@ -1,11 +1,12 @@ import 'package:cheminova/models/get_pd_response.dart'; import 'package:cheminova/provider/collect_kyc_provider.dart'; +import 'package:cheminova/utils/no_space_formatter.dart'; import 'package:csc_picker/csc_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -import '../constants/only_uppercase.dart'; +import '../utils/only_uppercase.dart'; import '../widgets/common_elevated_button.dart'; import '../widgets/common_text_form_field.dart'; @@ -68,10 +69,12 @@ class RetailerDetailsScreenState extends State { controller: value.nameController), const SizedBox(height: 15), CommonTextFormField( + // maxLength: 20, title: 'Email', - textCapitalization: TextCapitalization.none, - keyboardType: TextInputType.emailAddress, fillColor: Colors.white, + inputFormatters: [ + NoSpaceFormatter(), + ], validator: (String? value) { if (value!.isEmpty) { return 'Email cannot be empty'; diff --git a/lib/screens/select_distributer_screen.dart b/lib/screens/select_distributer_screen.dart index 720324a..13a57e8 100644 --- a/lib/screens/select_distributer_screen.dart +++ b/lib/screens/select_distributer_screen.dart @@ -2,6 +2,7 @@ import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/provider/pd_rd_provider.dart'; import 'package:cheminova/screens/add_products_screen.dart'; import 'package:cheminova/screens/add_sales_products_screen.dart'; +import 'package:cheminova/screens/visit_rd_pd_screen.dart'; import 'package:cheminova/utils/string_extension.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_app_bar.dart'; @@ -98,6 +99,18 @@ class _SelectDistributerScreenState extends State { : selectedDistributor!.kyc!.tradeName, pdRdId: selectedDistributor!.id!, ); + } else if (widget.task == 'Visit RD/PD') { + return VisitRdPdScreen( + distributorType: + selectedDistributorType == 'Principal Distributor' + ? 'PrincipalDistributor' + : 'RetailDistributor', + tradeName: selectedDistributorType == + 'Principal Distributor' + ? selectedDistributor!.shippingAddress!.tradeName + : selectedDistributor!.kyc!.tradeName, + pdRdId: selectedDistributor!.id!, + ); } else { return Container(); } @@ -164,7 +177,13 @@ class _SelectDistributerScreenState extends State { .map((PdRdResponseModel distributor) { return DropdownMenuItem( value: distributor, - child: Text(distributor.name!.capitalize()), + child: Text( + selectedDistributorType == + 'PrincipalDistributor' + ? distributor.shippingAddress!.tradeName + .capitalize() + : distributor.kyc!.tradeName.capitalize(), + ), ); }).toList(), onChanged: (value) { diff --git a/lib/screens/visit_rd_pd_screen.dart b/lib/screens/visit_rd_pd_screen.dart new file mode 100644 index 0000000..a23d901 --- /dev/null +++ b/lib/screens/visit_rd_pd_screen.dart @@ -0,0 +1,294 @@ +import 'package:cheminova/provider/visit_pdrd_provider.dart'; +import 'package:cheminova/widgets/common_drawer.dart'; +import 'package:flutter/material.dart'; +import 'package:cheminova/widgets/common_background.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import '../widgets/common_app_bar.dart'; +import '../widgets/common_elevated_button.dart'; +import '../widgets/common_text_form_field.dart'; +import 'package:image_picker/image_picker.dart'; + +class VisitRdPdScreen extends StatefulWidget { + final String? tradeName; + final String? pdRdId; + final String? distributorType; + const VisitRdPdScreen({ + super.key, + required this.tradeName, + required this.pdRdId, + required this.distributorType, + }); + + @override + State createState() => VisitRdPdScreenState(); +} + +class VisitRdPdScreenState extends State { + late VisitPdRdProvider _visitPdRdProvider; + final dateController = TextEditingController( + text: DateFormat('dd/MM/yyyy').format(DateTime.now())); + + final timeController = + TextEditingController(text: DateFormat('hh:mm a').format(DateTime.now())); + + final notesController = TextEditingController(); + final dealerController = TextEditingController(); + final meetingSummaryController = TextEditingController(); + final followUpActionsController = TextEditingController(); + final nextVisitDateController = TextEditingController(); + late TextEditingController retailerController = TextEditingController(); + + String selectedPurpose = 'Sales'; + List purposeOptions = ['Sales', 'Dues collection', 'Others']; + + Future _pickImage(ImageSource source) async { + final ImagePicker picker = ImagePicker(); + final XFile? image = await picker.pickImage(source: source); + if (image != null) { + // Handle the picked image + // For example, you could update a state variable or send it to your provider + print('Image picked: ${image.path}'); + // You might want to update your UI to show the selected image + setState(() { + notesController.text = image.path; // Just for demonstration + }); + } + } + + void _showImageSourceActionSheet() { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return SafeArea( + child: Wrap( + children: [ + ListTile( + leading: const Icon(Icons.camera_alt), + title: const Text('Take a photo'), + onTap: () { + Navigator.pop(context); + _pickImage(ImageSource.camera); + }, + ), + ListTile( + leading: const Icon(Icons.photo_library), + title: const Text('Choose from gallery'), + onTap: () { + Navigator.pop(context); + _pickImage(ImageSource.gallery); + }, + ), + ], + ), + ); + }, + ); + } + + Future _selectNextVisitDate() async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime.now().add(const Duration(days: 365)), + ); + if (picked != null) { + setState(() { + nextVisitDateController.text = DateFormat('dd/MM/yyyy').format(picked); + }); + } + } + + @override + void initState() { + _visitPdRdProvider = VisitPdRdProvider(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + retailerController = TextEditingController(text: widget.tradeName); + + return ChangeNotifierProvider( + create: (context) => _visitPdRdProvider, + child: CommonBackground( + child: Stack( + children: [ + Scaffold( + backgroundColor: Colors.transparent, + appBar: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: Column( + children: [ + const Text('Visit Retailers', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + Text(widget.tradeName ?? '', + style: const TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + ], + ), + backgroundColor: Colors.transparent, + elevation: 0, + ), + drawer: const CommonDrawer(), + body: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(20.0) + .copyWith(top: 30, bottom: 30), + margin: const EdgeInsets.symmetric(horizontal: 16.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + color: const Color(0xffB4D1E5).withOpacity(0.9), + borderRadius: BorderRadius.circular(26.0)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CommonTextFormField( + readOnly: true, + title: 'Select Retailer', + fillColor: Colors.grey[300], + controller: retailerController), + const SizedBox(height: 15), + CommonTextFormField( + title: 'Visit date', + readOnly: true, + fillColor: Colors.grey[300], + controller: dateController), + const SizedBox(height: 15), + CommonTextFormField( + title: 'Time', + readOnly: true, + fillColor: Colors.grey[300], + controller: timeController), + const SizedBox(height: 15), + DropdownButtonFormField( + decoration: const InputDecoration( + labelText: 'Purpose of visit', + fillColor: Colors.white, + filled: true, + ), + value: selectedPurpose, + items: purposeOptions.map((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + selectedPurpose = newValue!; + }); + }, + ), + const SizedBox(height: 15), + CommonTextFormField( + title: 'Meeting summary:', + fillColor: Colors.white, + maxLines: 3, + controller: meetingSummaryController), + const SizedBox(height: 15), + CommonTextFormField( + title: 'Follow-up Actions:', + fillColor: Colors.white, + maxLines: 3, + controller: followUpActionsController), + const SizedBox(height: 15), + GestureDetector( + onTap: _selectNextVisitDate, + child: AbsorbPointer( + child: CommonTextFormField( + title: 'Next visit date:', + readOnly: true, + fillColor: Colors.white, + controller: nextVisitDateController, + ), + ), + ), + const SizedBox(height: 15), + Row( + children: [ + Expanded( + child: CommonTextFormField( + title: 'Attach Documents/Photos', + fillColor: Colors.white, + controller: notesController), + ), + IconButton( + icon: const Icon(Icons.add_a_photo), + onPressed: _showImageSourceActionSheet, + ), + ], + ), + const SizedBox(height: 15), + Consumer( + builder: (context, value, child) => Align( + alignment: Alignment.center, + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SUBMIT', + backgroundColor: const Color(0xff004791), + onPressed: () { + value.submitVisitPdRd( + pdRdId: widget.pdRdId!, + distributorType: widget.distributorType!, + tradeName: widget.tradeName!, + visitDate: dateController.text, + visitTime: timeController.text, + meetingSummary: + meetingSummaryController.text, + visitPurpose: selectedPurpose, + followUp: followUpActionsController.text, + nextVisitDate: nextVisitDateController.text, + ); + }, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + Consumer(builder: (context, value, child) { + if (value.isLoading) { + return Container( + color: Colors.black.withOpacity(0.5), + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } + return const SizedBox.shrink(); + }), + ], + ), + ), + ); + } +} diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index a1a30cc..9dc6ef3 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -15,12 +15,13 @@ class ApiUrls { static const String getProducts = '${baseUrl}product/getAll/user/'; static const String getRd = 'inventory/distributors-TM/RetailDistributor'; static const String getPd = 'inventory/distributors-TM/PrincipalDistributor'; - static const String submitProducts = 'inventory/add-TM'; + static const String submitProducts = 'inventory/add'; static const String getSalesCoordinators = 'salescoordinator/getAll-TM'; static const String assignTask = 'task/assign-task'; static const String getProductsManual = 'productmanual/getall'; static const String getAllTasks = 'task/alltasks/'; static const String salesTaskUrl = '${baseUrl}product/getAll/user/'; static const String postSalesTaskUrl = '${baseUrl}sales/add-TM'; - static const String updateTaskInventoryUrl = ''; + static const String visitRdPd = '$baseUrl/visit'; + static const String allTaskByDate = '${baseUrl}task/alltask'; } diff --git a/lib/utils/no_space_formatter.dart b/lib/utils/no_space_formatter.dart new file mode 100644 index 0000000..964b23e --- /dev/null +++ b/lib/utils/no_space_formatter.dart @@ -0,0 +1,16 @@ +import 'package:flutter/services.dart'; + +class NoSpaceFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + // Filter out spaces from the input + String newText = newValue.text.replaceAll(' ', ''); + + // Return the new formatted value without spaces + return TextEditingValue( + text: newText, + selection: newValue.selection, + ); + } +} diff --git a/lib/constants/only_uppercase.dart b/lib/utils/only_uppercase.dart similarity index 100% rename from lib/constants/only_uppercase.dart rename to lib/utils/only_uppercase.dart