diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/models/Announcements_Response.dart b/lib/models/Announcements_Response.dart new file mode 100644 index 0000000..371462d --- /dev/null +++ b/lib/models/Announcements_Response.dart @@ -0,0 +1,28 @@ +class AnnouncementResponse { + final String id; + final String uniqueId; + final List sentTo; + final String message; + final DateTime createdAt; + final DateTime updatedAt; + + AnnouncementResponse({ + required this.id, + required this.uniqueId, + required this.sentTo, + required this.message, + required this.createdAt, + required this.updatedAt, + }); + + factory AnnouncementResponse.fromJson(Map json) { + return AnnouncementResponse( + id: json['_id'] ?? '', + uniqueId: json['uniqueId'] ?? '', + sentTo: List.from(json['sentTo']), + message: json['message'] ?? '', + createdAt: DateTime.parse(json['createdAt']), + updatedAt: DateTime.parse(json['updatedAt']), + ); + } +} diff --git a/lib/models/pd_rd_response_model.dart b/lib/models/pd_rd_response_model.dart new file mode 100644 index 0000000..2979b26 --- /dev/null +++ b/lib/models/pd_rd_response_model.dart @@ -0,0 +1,123 @@ +class PdRdResponseModel { + String? id; + String? uniqueId; + String? name; + ShippingAddress? shippingAddress; + Kyc? kyc; + + String? salesCoordinator; // Nullable property for SalesCoordinator + + PdRdResponseModel({ + this.id, + this.uniqueId, + this.name, + this.kyc, + this.shippingAddress, + this.salesCoordinator, // Initialize SalesCoordinator + }); + + factory PdRdResponseModel.fromJson(Map json) => + PdRdResponseModel( + id: json["_id"], + name: json["name"], + uniqueId: json["uniqueId"], + kyc: json["kyc"] != null ? Kyc.fromJson(json["kyc"]) : null, + shippingAddress: json['shippingAddress'] != null + ? ShippingAddress.fromJson(json['shippingAddress']) + : null, + salesCoordinator: json.containsKey('salesCoordinator') + ? json['salesCoordinator'] + : null, // Handle missing field + ); +} + +class ShippingAddress { + final String id; + final String street; + final String city; + final String state; + final String postalCode; + final String country; + final String panNumber; + final String tradeName; + final String gstNumber; + final bool isDefault; + + ShippingAddress({ + required this.id, + required this.street, + required this.city, + required this.state, + required this.postalCode, + required this.country, + required this.panNumber, + required this.tradeName, + required this.gstNumber, + required this.isDefault, + }); + + factory ShippingAddress.fromJson(Map json) { + return ShippingAddress( + id: json['_id'] ?? '', + street: json['street'] ?? '', + city: json['city'] ?? '', + state: json['state'] ?? '', + postalCode: json['postalCode'] ?? '', + country: json['country'] ?? '', + panNumber: json['panNumber'] ?? '', + tradeName: json['tradeName'] ?? '', + gstNumber: json['gstNumber'] ?? '', + isDefault: json['isDefault'] ?? false, + ); + } +} + +class Kyc { + final String id; + final String street; + final String city; + final String state; + final String postalCode; + final String country; + final String panNumber; + final String tradeName; + final String gstNumber; + final bool isDefault; + final String? panImgUrl; // New field for PAN image URL + final String? aadharImgUrl; // New field for Aadhar image URL + final String? gstImgUrl; // New field for GST image URL + + Kyc({ + required this.id, + required this.street, + required this.city, + required this.state, + required this.postalCode, + required this.country, + required this.panNumber, + required this.tradeName, + required this.gstNumber, + required this.isDefault, + this.panImgUrl, + this.aadharImgUrl, + this.gstImgUrl, + }); + + factory Kyc.fromJson(Map json) { + return Kyc( + id: json['_id'] ?? '', + street: json['street'] ?? '', + city: json['city'] ?? '', + state: json['state'] ?? '', + postalCode: json['postalCode'] ?? '', + country: json['country'] ?? '', + panNumber: json['panNumber'] ?? '', + tradeName: json['trade_name'] ?? '', + gstNumber: json['gstNumber'] ?? '', + isDefault: json['isDefault'] ?? false, + panImgUrl: json['pan_img']?['url'], + aadharImgUrl: json['aadhar_img']?['url'], + gstImgUrl: json['gst_img']?['url'], + ); + } +} diff --git a/lib/models/task_model.dart b/lib/models/task_model.dart new file mode 100644 index 0000000..e55d3f8 --- /dev/null +++ b/lib/models/task_model.dart @@ -0,0 +1,78 @@ +class TaskModel { + final String id; + final String taskId; + final String task; + final String? note; + final String taskStatus; + final String? taskPriority; + final DateTime taskDueDate; + final TaskAssignedTo taskAssignedTo; + final String? taskAssignedBy; + final String? addedFor; + final String? addedForId; + final String? tradename; + final DateTime? createdAt; + final DateTime? updatedAt; + final int? version; + + TaskModel({ + required this.id, + required this.taskId, + required this.task, + this.note, + required this.taskStatus, + this.taskPriority, + required this.taskDueDate, + required this.taskAssignedTo, + this.taskAssignedBy, + this.addedFor, + this.addedForId, + this.tradename, + this.createdAt, + this.updatedAt, + this.version, + }); + + factory TaskModel.fromJson(Map json) { + return TaskModel( + id: json['_id'], + taskId: json['taskId'], + task: json['task'], + note: json['note'], + taskStatus: json['taskStatus'], + taskPriority: json['taskPriority'], + taskDueDate: DateTime.parse(json['taskDueDate']), + taskAssignedTo: TaskAssignedTo.fromJson(json['taskAssignedTo']), + taskAssignedBy: json['taskAssignedBy'], + addedFor: json['addedFor'], + addedForId: json['addedForId'], + tradename: json['tradename'], + createdAt: DateTime.parse(json['createdAt']), + updatedAt: DateTime.parse(json['updatedAt']), + version: json['__v'], + ); + } +} + +class TaskAssignedTo { + final String id; + final String name; + final String mobileNumber; + final String email; + + TaskAssignedTo({ + required this.id, + required this.name, + required this.mobileNumber, + required this.email, + }); + + factory TaskAssignedTo.fromJson(Map json) { + return TaskAssignedTo( + id: json['_id'], + name: json['name'], + mobileNumber: json['mobileNumber'], + email: json['email'], + ); + } +} diff --git a/lib/provider/Announcement_Provider.dart b/lib/provider/Announcement_Provider.dart new file mode 100644 index 0000000..91322aa --- /dev/null +++ b/lib/provider/Announcement_Provider.dart @@ -0,0 +1,41 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import '../models/Announcements_Response.dart'; +import '../services/api_client.dart'; +import '../services/api_urls.dart'; + +class AnnouncementProvider extends ChangeNotifier { + AnnouncementProvider() { + getAnnouncements(); // Fetch announcements when the provider is initialized + } + + final _apiClient = ApiClient(); // Use the API client to fetch data + List announcementList = []; // List to hold announcement data + + bool _isLoading = false; // Loading state + + bool get isLoading => _isLoading; // Getter for loading state + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); // Notify listeners when loading state changes + } + + Future getAnnouncements() async { + setLoading(true); // Set loading to true before the request + try { + Response response = await _apiClient.get(ApiUrls.announcementUrl); // Fetch announcements + setLoading(false); // Set loading to false after the request completes + if (response.statusCode == 200) { + final List data = response.data; + announcementList = data + .map((item) => AnnouncementResponse.fromJson(item)) + .toList(); // Map JSON data to the announcement model + notifyListeners(); // Notify listeners after data is updated + } + } catch (e) { + setLoading(false); // Handle errors and stop loading state + // Optionally handle errors here (e.g., log error, show message) + } + } +} diff --git a/lib/provider/products_provider.dart b/lib/provider/products_provider.dart index 9e0098f..07422c7 100644 --- a/lib/provider/products_provider.dart +++ b/lib/provider/products_provider.dart @@ -45,7 +45,7 @@ class ProductProvider extends ChangeNotifier { if (response.statusCode == 200) { productResponse = ProductResponse.fromJson(response.data); productList = productResponse!.products! - .map((product) => ProductModel(sku: product.sKU!, productName: product.name!)) + .map((product) => ProductModel(sku: product.sKU!, productName: product.name!,id: product.sId!)) .toList(); notifyListeners(); // Notify listeners to update the UI with the product list } @@ -59,6 +59,7 @@ class ProductProvider extends ChangeNotifier { // Send POST request to submit products Response response = await _apiClient.post(ApiUrls.submitProductUrl, data: json.encode({ + "addedFor": distributorType.replaceAll(' ', ''), "addedForId": pdRdId, "products": selectedProducts.map((e) => e.toJson()).toList() @@ -116,6 +117,7 @@ class ProductProvider extends ChangeNotifier { // Model class for Product class ProductModel { + String id; // ID of the product String sku; // SKU of the product String productName; // Name of the product int? sale; // Sale quantity (optional) @@ -123,7 +125,9 @@ class ProductModel { // Constructor for ProductModel ProductModel( - {required this.sku, + { + required this.id, + required this.sku, required this.productName, this.sale, this.inventory}); @@ -131,6 +135,7 @@ class ProductModel { // Factory method to create a ProductModel from JSON factory ProductModel.fromJson(Map json) { return ProductModel( + id:json['_id'], sku: json['SKU'], productName: json['ProductName'], sale: json['Sale'], @@ -142,6 +147,7 @@ class ProductModel { // Convert ProductModel to JSON format for API requests Map toJson() { return { + '_id': id, 'SKU': sku, 'ProductName': productName, 'Sale': sale, diff --git a/lib/provider/task_provider.dart b/lib/provider/task_provider.dart new file mode 100644 index 0000000..9a20336 --- /dev/null +++ b/lib/provider/task_provider.dart @@ -0,0 +1,61 @@ +import 'package:cheminova/models/pd_rd_response_model.dart'; +import 'package:cheminova/models/task_model.dart'; +import 'package:cheminova/screens/data_submit_successfull.dart'; +import 'package:cheminova/services/api_client.dart'; +import 'package:cheminova/services/api_urls.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class TaskProvider extends ChangeNotifier { + bool _isLoading = false; + PdRdResponseModel? _selectedSalesCoordinator; + String? _selectedTask; + String? _selectedPriority; + String _selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now()); + List _salesCoordinators = []; + List _pdList = []; + List _rdList = []; + List _taskModelList = []; + PdRdResponseModel? _selectedDistributor; + final TextEditingController _noteController = TextEditingController(); + final _apiClient = ApiClient(); + + bool get isLoading => _isLoading; + PdRdResponseModel? get selectedSalesCoordinator => _selectedSalesCoordinator; + String? get selectedTask => _selectedTask; + String? get selectedPriority => _selectedPriority; + String get selectedDate => _selectedDate; + List get salesCoordinators => _salesCoordinators; + TextEditingController get noteController => _noteController; + List get pdList => _pdList; + List get rdList => _rdList; + List get taskModelList => _taskModelList; + PdRdResponseModel? get selectedDistributor => _selectedDistributor; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + + + Future getAllTaskByDate(DateTime date) async { + 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/screens/Add_products_screen.dart b/lib/screens/Add_products_screen.dart index 8117e58..2051075 100644 --- a/lib/screens/Add_products_screen.dart +++ b/lib/screens/Add_products_screen.dart @@ -324,7 +324,7 @@ class _ProductBlockState extends State { sku: widget.product.sku, productName: widget.product.productName, sale: sale, - inventory: inventory, + inventory: inventory, id: widget.product.id, )); } else { errorMessage = saleError ?? inventoryError; diff --git a/lib/screens/Announcements_Screen.dart b/lib/screens/Announcements_Screen.dart new file mode 100644 index 0000000..02f5da6 --- /dev/null +++ b/lib/screens/Announcements_Screen.dart @@ -0,0 +1,153 @@ +import 'package:cheminova/provider/Announcement_Provider.dart'; +import 'package:flutter/material.dart'; +import 'package:cheminova/widgets/common_background.dart'; +import 'package:cheminova/widgets/common_drawer.dart'; +import 'package:cheminova/widgets/common_app_bar.dart'; +import 'package:cheminova/widgets/common_elevated_button.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +import '../models/Announcements_Response.dart'; + +// Main screen that displays notifications +class AnnouncementsScreen extends StatefulWidget { + const AnnouncementsScreen({super.key}); + + @override + State createState() => AnnouncementsScreenState(); +} + +class AnnouncementsScreenState extends State { + late AnnouncementProvider _announcementProvider; + + @override + void initState() { + // Initialize the NotificationProvider in the state when the screen is created + _announcementProvider = AnnouncementProvider(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + // Use ChangeNotifierProvider to supply NotificationProvider to the widget tree + return ChangeNotifierProvider( + create: (context) => _announcementProvider, + child: CommonBackground( + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + // Pop the screen when the back button is pressed + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ), + ], + title: const Text('Announcements', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, + ), + drawer: const CommonDrawer(), + // Consumer listens for changes in the NotificationProvider + body: Consumer( + builder: (context, value, child) => value.isLoading + // Display a loading indicator if notifications are still loading + ? const Center(child: CircularProgressIndicator()) + // Show the list of notifications once loaded + : MyListView(value: value), + ), + ), + ), + ); + } +} + +// Function that builds a button for a product +Widget buildProductButton(String productName) { + return Padding( + padding: const EdgeInsets.only(bottom: 15), + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: productName, + backgroundColor: const Color(0xff004791), + onPressed: () { + // Log when the button is pressed + debugPrint('$productName pressed'); + }, + ), + ); +} + +// Widget that displays a list of notifications grouped by date +class MyListView extends StatelessWidget { + final AnnouncementProvider value; + + const MyListView({super.key, required this.value}); + + @override + Widget build(BuildContext context) { + // Group notifications by their creation date + Map> groupedNotifications = {}; + + // Iterate over the notification list and group by formatted date + for (var notification in value.announcementList.reversed) { + String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt.toString())); + if (!groupedNotifications.containsKey(date)) { + groupedNotifications[date] = []; + } + groupedNotifications[date]!.add(notification); + } + + // Build a ListView for grouped notifications + return ListView.builder( + + padding: const EdgeInsets.only(top: 15), + itemCount: groupedNotifications.length, // Number of date groups + itemBuilder: (context, index) { + String date = groupedNotifications.keys.elementAt(index); + List notificationsForDate = groupedNotifications[date]!; + + return Padding( + padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Display the date for each group of notifications + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + date, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + // Display each notification in an expandable tile + ...notificationsForDate.map((item) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: ExpansionTile( + collapsedBackgroundColor: Colors.white, + backgroundColor: Colors.white, + trailing: const SizedBox.shrink(), // Remove trailing icon + title: const Text( + "New Announcement", + style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), + subtitle: Text(item.message), + ), + )), + ], + ), + ); + }, + ); + } +} diff --git a/lib/screens/Update_inventorytask_screen.dart b/lib/screens/Update_inventorytask_screen.dart index bfc16ce..1e29918 100644 --- a/lib/screens/Update_inventorytask_screen.dart +++ b/lib/screens/Update_inventorytask_screen.dart @@ -121,7 +121,7 @@ class _UpdateInventoryTaskScreenState extends State { distributorType: tasksList.addedFor!, // Pass distributor type inventoryId: tasksList.sId!, // Pass inventory ID tradeName: tasksList.tradeName ?? '', // Pass trader name - pdRdId: tasksList.sId!, // Pass PD/RD ID + pdRdId: tasksList.addedForId!, // Pass PD/RD ID ), ), ); diff --git a/lib/screens/add_sales_product_screen.dart b/lib/screens/add_sales_product_screen.dart index 81b43a2..b9384ca 100644 --- a/lib/screens/add_sales_product_screen.dart +++ b/lib/screens/add_sales_product_screen.dart @@ -63,121 +63,124 @@ class _AddSalesProductScreenState extends State { return PopScope( canPop: true, child: CommonBackground(// Common background for the screen - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: CommonAppBar(// Custom AppBar with back button and title - actions: [ - IconButton( - onPressed: () => Navigator.pop(context), - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ) - ], - title: Text( - '${widget.distributorType}\n${widget.tradeName}', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek', + child: Form( + key: formKey, + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: CommonAppBar(// Custom AppBar with back button and title + actions: [ + IconButton( + onPressed: () => Navigator.pop(context), + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), + ) + ], + title: Text( + '${widget.distributorType}\n${widget.tradeName}', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek', + ), + ), + backgroundColor: Colors.transparent, + elevation: 0, + ), + drawer: const CommonDrawer(), + bottomNavigationBar: Consumer( + builder: (context, value, child) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: value.tasksList.isEmpty + ? Alignment.center + : Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [// Floating button to add products + FloatingActionButton.extended( + onPressed: () => _showProductSelectionBottomSheet(context), + backgroundColor: Colors.white, + icon: const Icon(Icons.add, color: Colors.black), + label: const Text('Add Products', style: TextStyle(color: Colors.black)), + ), + if (value.selectedProducts.isNotEmpty) ...[ + const SizedBox(height: 16.0), + // Submit button that appears after products are selected + CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: 'SUBMIT', + backgroundColor: const Color(0xff004791), + onPressed: () => _submitProducts(value), + ), + ], + ], + ), + ), + ), + ], ), ), - backgroundColor: Colors.transparent, - elevation: 0, - ), - drawer: const CommonDrawer(), - bottomNavigationBar: Consumer( - builder: (context, value, child) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - Align( - alignment: value.tasksList.isEmpty - ? Alignment.center - : Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [// Floating button to add products - FloatingActionButton.extended( - onPressed: () => _showProductSelectionBottomSheet(context), - backgroundColor: Colors.white, - icon: const Icon(Icons.add, color: Colors.black), - label: const Text('Add Products', style: TextStyle(color: Colors.black)), - ), - if (value.selectedProducts.isNotEmpty) ...[ - const SizedBox(height: 16.0), - // Submit button that appears after products are selected - CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'SUBMIT', - backgroundColor: const Color(0xff004791), - onPressed: () => _submitProducts(value), + body: Consumer(// Displays the list of selected products or loading indicator + builder: (context, value, child) { + return Stack( + children: [ + Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField(// Date selection field + controller: dateController, + readOnly: true, + decoration: const InputDecoration( + labelText: 'Date', + fillColor: Colors.white, + filled: true, + border: InputBorder.none, + suffixIcon: Icon(Icons.calendar_today), + ), + ), + ), + // Display selected products list + if (value.selectedProducts.isNotEmpty) + Expanded( + child: ListView.builder( + itemCount: value.selectedProducts.length, + itemBuilder: (context, index) { + return ProductBlock( + onUpdate: (updatedProduct) { + setState(() { + value.selectedProducts[index] = updatedProduct; + }); + }, + onRemove: () { + setState(() { + value.selectedProducts.removeAt(index); + }); + }, + product: value.selectedProducts[index], + ); + }, + ), ), - ], ], ), - ), - ), - ], - ), - ), - body: Consumer(// Displays the list of selected products or loading indicator - builder: (context, value, child) { - return Stack( - children: [ - Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField(// Date selection field - controller: dateController, - readOnly: true, - decoration: const InputDecoration( - labelText: 'Date', - fillColor: Colors.white, - filled: true, - border: InputBorder.none, - suffixIcon: Icon(Icons.calendar_today), - ), - ), + // Loading indicator + if (value.isLoading) + Container( + color: Colors.black12, + child: const Center(child: CircularProgressIndicator()), ), - // Display selected products list - if (value.selectedProducts.isNotEmpty) - Expanded( - child: ListView.builder( - itemCount: value.selectedProducts.length, - itemBuilder: (context, index) { - return ProductBlock( - onUpdate: (updatedProduct) { - setState(() { - value.selectedProducts[index] = updatedProduct; - }); - }, - onRemove: () { - setState(() { - value.selectedProducts.removeAt(index); - }); - }, - product: value.selectedProducts[index], - ); - }, - ), - ), - ], - ), - // Loading indicator - if (value.isLoading) - Container( - color: Colors.black12, - child: const Center(child: CircularProgressIndicator()), - ), - ], - ); - }, + ], + ); + }, + ), ), ), ), diff --git a/lib/screens/calendar_screen.dart b/lib/screens/calendar_screen.dart index 7e54c80..3c229b3 100644 --- a/lib/screens/calendar_screen.dart +++ b/lib/screens/calendar_screen.dart @@ -1,43 +1,74 @@ +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'; // Custom AppBar widget -import '../widgets/common_background.dart'; // Custom background widget -import '../widgets/common_drawer.dart'; // Custom drawer widget +import '../widgets/common_app_bar.dart'; +import '../widgets/common_background.dart'; +import '../widgets/common_drawer.dart'; -// Global variables to track the focused and selected dates in the calendar DateTime _focusedDay = DateTime.now(); -DateTime? _selectedDay = DateTime.now(); +DateTime _selectedDay = DateTime.now(); -// Main Calendar screen -class CalendarScreen extends StatelessWidget { +class CalendarScreen extends StatefulWidget { const CalendarScreen({super.key}); + @override + State createState() => _CalendarScreenState(); +} + +class _CalendarScreenState extends State { + late TaskProvider taskProvider; + + @override + void initState() { + taskProvider = TaskProvider(); + super.initState(); + } + @override Widget build(BuildContext context) { - return CommonBackground( // Wrapper for common background styling - child: Scaffold( - appBar: CommonAppBar( // Custom AppBar - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); // Go back to the previous screen - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), + return ChangeNotifierProvider( + create: (context) => taskProvider, + child: Consumer( + builder: (context, value, child) => CommonBackground( + child: Scaffold( + appBar: CommonAppBar( + actions: [ + 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: Stack( + children: [ + Column(children: [ + const CalendarWidget(), + TaskList(taskProvider: value) + ]), + 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(), + ), + ), + ], ), - ], - title: const Text('Calendar'), // Title of the AppBar - backgroundColor: Colors.transparent, // Transparent background for the AppBar - elevation: 0, - ), - backgroundColor: Colors.transparent, // Transparent background for the Scaffold - drawer: const CommonDrawer(), // Custom drawer widget - body: const SingleChildScrollView( // Scrollable view to accommodate the calendar and events list - child: Column( - children: [ - CalendarWidget(), // Calendar widget to show calendar dates - EventsList(), // List of events associated with the selected date - ], ), ), ), @@ -45,7 +76,6 @@ class CalendarScreen extends StatelessWidget { } } -// Calendar widget that shows the calendar dates class CalendarWidget extends StatefulWidget { const CalendarWidget({super.key}); @@ -56,117 +86,142 @@ class CalendarWidget extends StatefulWidget { class _CalendarWidgetState extends State { @override Widget build(BuildContext context) { - return Card( // Card UI to display the calendar inside - margin: const EdgeInsets.all(16), // Margin around the card + return Card( + margin: const EdgeInsets.all(16), child: Padding( - padding: const EdgeInsets.all(16), // Padding inside the card - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left - children: [ - const Text( - 'Calendar', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // Calendar heading - ), - const Text( - 'Month/Year', - style: TextStyle(fontSize: 14, color: Colors.grey), // Display current month and year - ), - const SizedBox(height: 10), - TableCalendar( // TableCalendar widget to show date selection - firstDay: DateTime.utc(1900, 5, 1), // Earliest selectable date - lastDay: DateTime.utc(2900, 5, 1), // Latest selectable date - focusedDay: _focusedDay, // Currently focused day - selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); // Highlight selected day - }, - onDaySelected: (selectedDay, focusedDay) { - setState(() { // Update selected and focused days - _selectedDay = selectedDay; - _focusedDay = focusedDay; - }); - }, - onPageChanged: (focusedDay) { - _focusedDay = focusedDay; // Change focused day when calendar page changes - }, - ), - ], + padding: const EdgeInsets.all(16), + 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; + }, ), ), ); } } -// Widget to display a list of events -class EventsList extends StatelessWidget { - const EventsList({super.key}); +class TaskList extends StatefulWidget { + final TaskProvider taskProvider; + + const TaskList({super.key, required this.taskProvider}); + + @override + State createState() => _TaskListState(); +} + +class _TaskListState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.taskProvider.getAllTaskByDate(_selectedDay); + }); + } @override Widget build(BuildContext context) { - return const Card( // Card UI for the event list - margin: EdgeInsets.all(16), // Margin around the card - child: Padding( - padding: EdgeInsets.all(16), // Padding inside the card - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left - children: [ - Text( - 'Events List', // Heading for the events list - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + return (widget.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: widget.taskProvider.taskModelList.length, + itemBuilder: (context, index) { + return _taskView( + task: widget.taskProvider.taskModelList[index]); + }, ), - SizedBox(height: 10), - // Display individual event items - 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 for displaying individual event items -class EventItem extends StatelessWidget { - final String date; // Event date - final String event; // Event description - - const EventItem({super.key, required this.date, required this.event}); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(12), // Padding inside the event container - decoration: BoxDecoration( - color: Colors.blue[50], // Light blue background for the event item - borderRadius: BorderRadius.circular(8), // Rounded corners for the container - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left - children: [ - Text('Date: $date'), // Display event date - Text( - 'Event: $event', // Display event description - style: const TextStyle(fontWeight: FontWeight.bold), + 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: 8), - ElevatedButton( // Button to view event details - onPressed: () { - // Add view details functionality here - }, - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, // Text color - backgroundColor: Colors.blue[800], // Button background color - ), - child: const Text('VIEW DETAILS'), // Button label - ), - ], - ), + ), + const SizedBox(height: 10), + ], ); } } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 8d21701..4e019c5 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,5 +1,6 @@ import 'package:cheminova/notification_services.dart'; import 'package:cheminova/provider/home_provider.dart'; +import 'package:cheminova/screens/Announcements_Screen.dart'; import 'package:cheminova/screens/Update_inventorytask_screen.dart'; import 'package:cheminova/screens/calendar_screen.dart'; import 'package:cheminova/screens/daily_tasks_screen.dart'; @@ -215,6 +216,20 @@ class _HomePageState extends State { }, ), const SizedBox(height: 5), + _buildCustomCard( + 'Announcements', + "View Announcements", + screenWidth, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AnnouncementsScreen(), + ), + ); + }, + ), + const SizedBox(height: 5), // Row containing cards for calendar and products manual Row( children: [ diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index 6f4904c..f454fc4 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -6,22 +6,27 @@ class ApiUrls { 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'; - static const String forgotPasswordUrl = '${baseUrl}salescoordinator/forgot-password'; - static const String leaveAttendance = '${baseUrl}v1/markleave/salescoordinator'; + static const String changePasswordUrl = + '${baseUrl}salescoordinator/password/update'; + static const String forgotPasswordUrl = + '${baseUrl}salescoordinator/forgot-password'; + static const String leaveAttendance = + '${baseUrl}v1/markleave/salescoordinator'; static const String logOutUrl = '${baseUrl}salescoordinator/logout'; - static const String rejectedApplication = '${baseUrl}kyc/getAllrejected'; - static const String notificationUrl = '${baseUrl}/get-notification-sc'; - static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc'; - static const String getProducts = '${baseUrl}product/getAll/user'; - static const String getPdRdUrl = '${baseUrl}inventory/distributors-SC/'; - static const String submitProductUrl = '${baseUrl}inventory/add-SC'; - static const String selectTaskUrl = '${baseUrl}task/tasks'; - static const String dailyTaskUrl = '${baseUrl}task/tasks/'; - static const String kycSelectTaskUrl = '${baseUrl}task/task/type/Collect KYC'; - static const String updateTaskInventoryUrl = '${baseUrl}task/update-task-status/'; - static const String getProductsManual = '${baseUrl}productmanual/getall'; - static const String salesTaskUrl = '${baseUrl}product/getAll/user/'; - static const String postSalesTaskUrl = '${baseUrl}sales/add-SC'; - static const String submitVisitUrl = '${baseUrl}visit'; + static const String rejectedApplication = '${baseUrl}kyc/getAllrejected'; + static const String notificationUrl = '${baseUrl}/get-notification-sc'; + static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc'; + static const String getProducts = '${baseUrl}product/getAll/user'; + static const String getPdRdUrl = '${baseUrl}inventory/distributors-SC/'; + static const String submitProductUrl = '${baseUrl}inventory/add'; + static const String selectTaskUrl = '${baseUrl}task/tasks'; + static const String dailyTaskUrl = '${baseUrl}task/tasks/'; + static const String kycSelectTaskUrl = '${baseUrl}task/task/type/Collect KYC'; + static const String updateTaskInventoryUrl = '${baseUrl}task/update-task-status/'; + static const String getProductsManual = '${baseUrl}productmanual/getall'; + static const String salesTaskUrl = '${baseUrl}product/getAll/user/'; + static const String postSalesTaskUrl = '${baseUrl}sales/add-SC'; + static const String submitVisitUrl = '${baseUrl}visit'; + static const String allTaskByDate = '${baseUrl}task/alltask'; + static const String announcementUrl = '${baseUrl}announcement/SCs'; }