api calendar and announcement intergrate

This commit is contained in:
Vaibhav 2024-10-14 17:16:10 +05:30
parent 86a9b751ba
commit c577c7005f
14 changed files with 826 additions and 255 deletions

3
devtools_options.yaml Normal file
View File

@ -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:

View File

@ -0,0 +1,28 @@
class AnnouncementResponse {
final String id;
final String uniqueId;
final List<String> 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<String, dynamic> json) {
return AnnouncementResponse(
id: json['_id'] ?? '',
uniqueId: json['uniqueId'] ?? '',
sentTo: List<String>.from(json['sentTo']),
message: json['message'] ?? '',
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
);
}
}

View File

@ -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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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'],
);
}
}

View File

@ -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<String, dynamic> 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<String, dynamic> json) {
return TaskAssignedTo(
id: json['_id'],
name: json['name'],
mobileNumber: json['mobileNumber'],
email: json['email'],
);
}
}

View File

@ -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<AnnouncementResponse> 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<void> 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<dynamic> 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)
}
}
}

View File

@ -45,7 +45,7 @@ class ProductProvider extends ChangeNotifier {
if (response.statusCode == 200) { if (response.statusCode == 200) {
productResponse = ProductResponse.fromJson(response.data); productResponse = ProductResponse.fromJson(response.data);
productList = productResponse!.products! 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(); .toList();
notifyListeners(); // Notify listeners to update the UI with the product list 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 // Send POST request to submit products
Response response = await _apiClient.post(ApiUrls.submitProductUrl, Response response = await _apiClient.post(ApiUrls.submitProductUrl,
data: json.encode({ data: json.encode({
"addedFor": distributorType.replaceAll(' ', ''), "addedFor": distributorType.replaceAll(' ', ''),
"addedForId": pdRdId, "addedForId": pdRdId,
"products": selectedProducts.map((e) => e.toJson()).toList() "products": selectedProducts.map((e) => e.toJson()).toList()
@ -116,6 +117,7 @@ class ProductProvider extends ChangeNotifier {
// Model class for Product // Model class for Product
class ProductModel { class ProductModel {
String id; // ID of the product
String sku; // SKU of the product String sku; // SKU of the product
String productName; // Name of the product String productName; // Name of the product
int? sale; // Sale quantity (optional) int? sale; // Sale quantity (optional)
@ -123,7 +125,9 @@ class ProductModel {
// Constructor for ProductModel // Constructor for ProductModel
ProductModel( ProductModel(
{required this.sku, {
required this.id,
required this.sku,
required this.productName, required this.productName,
this.sale, this.sale,
this.inventory}); this.inventory});
@ -131,6 +135,7 @@ class ProductModel {
// Factory method to create a ProductModel from JSON // Factory method to create a ProductModel from JSON
factory ProductModel.fromJson(Map<String, dynamic> json) { factory ProductModel.fromJson(Map<String, dynamic> json) {
return ProductModel( return ProductModel(
id:json['_id'],
sku: json['SKU'], sku: json['SKU'],
productName: json['ProductName'], productName: json['ProductName'],
sale: json['Sale'], sale: json['Sale'],
@ -142,6 +147,7 @@ class ProductModel {
// Convert ProductModel to JSON format for API requests // Convert ProductModel to JSON format for API requests
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'_id': id,
'SKU': sku, 'SKU': sku,
'ProductName': productName, 'ProductName': productName,
'Sale': sale, 'Sale': sale,

View File

@ -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<PdRdResponseModel> _salesCoordinators = [];
List<PdRdResponseModel> _pdList = [];
List<PdRdResponseModel> _rdList = [];
List<TaskModel> _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<PdRdResponseModel> get salesCoordinators => _salesCoordinators;
TextEditingController get noteController => _noteController;
List<PdRdResponseModel> get pdList => _pdList;
List<PdRdResponseModel> get rdList => _rdList;
List<TaskModel> get taskModelList => _taskModelList;
PdRdResponseModel? get selectedDistributor => _selectedDistributor;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
Future<void> 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<TaskModel> data = (response.data['tasks'] as List)
.map((json) => TaskModel.fromJson(json))
.toList();
_taskModelList = data;
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
}

View File

@ -324,7 +324,7 @@ class _ProductBlockState extends State<ProductBlock> {
sku: widget.product.sku, sku: widget.product.sku,
productName: widget.product.productName, productName: widget.product.productName,
sale: sale, sale: sale,
inventory: inventory, inventory: inventory, id: widget.product.id,
)); ));
} else { } else {
errorMessage = saleError ?? inventoryError; errorMessage = saleError ?? inventoryError;

View File

@ -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<AnnouncementsScreen> createState() => AnnouncementsScreenState();
}
class AnnouncementsScreenState extends State<AnnouncementsScreen> {
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<AnnouncementProvider>(
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<String, List<AnnouncementResponse>> 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<AnnouncementResponse> 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),
),
)),
],
),
);
},
);
}
}

View File

@ -121,7 +121,7 @@ class _UpdateInventoryTaskScreenState extends State<UpdateInventoryTaskScreen> {
distributorType: tasksList.addedFor!, // Pass distributor type distributorType: tasksList.addedFor!, // Pass distributor type
inventoryId: tasksList.sId!, // Pass inventory ID inventoryId: tasksList.sId!, // Pass inventory ID
tradeName: tasksList.tradeName ?? '', // Pass trader name tradeName: tasksList.tradeName ?? '', // Pass trader name
pdRdId: tasksList.sId!, // Pass PD/RD ID pdRdId: tasksList.addedForId!, // Pass PD/RD ID
), ),
), ),
); );

View File

@ -63,121 +63,124 @@ class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
return PopScope( return PopScope(
canPop: true, canPop: true,
child: CommonBackground(// Common background for the screen child: CommonBackground(// Common background for the screen
child: Scaffold( child: Form(
backgroundColor: Colors.transparent, key: formKey,
appBar: CommonAppBar(// Custom AppBar with back button and title child: Scaffold(
actions: [ backgroundColor: Colors.transparent,
IconButton( appBar: CommonAppBar(// Custom AppBar with back button and title
onPressed: () => Navigator.pop(context), actions: [
icon: Image.asset('assets/Back_attendance.png'), IconButton(
padding: const EdgeInsets.only(right: 20), 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, title: Text(
style: const TextStyle( '${widget.distributorType}\n${widget.tradeName}',
fontSize: 20, textAlign: TextAlign.center,
color: Colors.black, style: const TextStyle(
fontWeight: FontWeight.w400, fontSize: 20,
fontFamily: 'Anek', color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek',
),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
drawer: const CommonDrawer(),
bottomNavigationBar: Consumer<AddSalesProvider>(
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, body: Consumer<AddSalesProvider>(// Displays the list of selected products or loading indicator
elevation: 0, builder: (context, value, child) {
), return Stack(
drawer: const CommonDrawer(), children: [
bottomNavigationBar: Consumer<AddSalesProvider>( Column(
builder: (context, value, child) => Column( children: [
mainAxisSize: MainAxisSize.min, Padding(
children: [ padding: const EdgeInsets.all(8.0),
Align( child: TextFormField(// Date selection field
alignment: value.tasksList.isEmpty controller: dateController,
? Alignment.center readOnly: true,
: Alignment.bottomCenter, decoration: const InputDecoration(
child: Padding( labelText: 'Date',
padding: const EdgeInsets.all(16.0), fillColor: Colors.white,
child: Column( filled: true,
mainAxisSize: MainAxisSize.min, border: InputBorder.none,
children: [// Floating button to add products suffixIcon: Icon(Icons.calendar_today),
FloatingActionButton.extended( ),
onPressed: () => _showProductSelectionBottomSheet(context), ),
backgroundColor: Colors.white, ),
icon: const Icon(Icons.add, color: Colors.black), // Display selected products list
label: const Text('Add Products', style: TextStyle(color: Colors.black)), if (value.selectedProducts.isNotEmpty)
), Expanded(
if (value.selectedProducts.isNotEmpty) ...[ child: ListView.builder(
const SizedBox(height: 16.0), itemCount: value.selectedProducts.length,
// Submit button that appears after products are selected itemBuilder: (context, index) {
CommonElevatedButton( return ProductBlock(
borderRadius: 30, onUpdate: (updatedProduct) {
width: double.infinity, setState(() {
height: kToolbarHeight - 10, value.selectedProducts[index] = updatedProduct;
text: 'SUBMIT', });
backgroundColor: const Color(0xff004791), },
onPressed: () => _submitProducts(value), onRemove: () {
setState(() {
value.selectedProducts.removeAt(index);
});
},
product: value.selectedProducts[index],
);
},
),
), ),
],
], ],
), ),
), // Loading indicator
), if (value.isLoading)
], Container(
), color: Colors.black12,
), child: const Center(child: CircularProgressIndicator()),
body: Consumer<AddSalesProvider>(// 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],
);
},
),
),
],
),
// Loading indicator
if (value.isLoading)
Container(
color: Colors.black12,
child: const Center(child: CircularProgressIndicator()),
),
],
);
},
), ),
), ),
), ),

View File

@ -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:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:table_calendar/table_calendar.dart'; import 'package:table_calendar/table_calendar.dart';
import '../widgets/common_app_bar.dart'; // Custom AppBar widget import '../widgets/common_app_bar.dart';
import '../widgets/common_background.dart'; // Custom background widget import '../widgets/common_background.dart';
import '../widgets/common_drawer.dart'; // Custom drawer widget import '../widgets/common_drawer.dart';
// Global variables to track the focused and selected dates in the calendar
DateTime _focusedDay = DateTime.now(); DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay = DateTime.now(); DateTime _selectedDay = DateTime.now();
// Main Calendar screen class CalendarScreen extends StatefulWidget {
class CalendarScreen extends StatelessWidget {
const CalendarScreen({super.key}); const CalendarScreen({super.key});
@override
State<CalendarScreen> createState() => _CalendarScreenState();
}
class _CalendarScreenState extends State<CalendarScreen> {
late TaskProvider taskProvider;
@override
void initState() {
taskProvider = TaskProvider();
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( // Wrapper for common background styling return ChangeNotifierProvider(
child: Scaffold( create: (context) => taskProvider,
appBar: CommonAppBar( // Custom AppBar child: Consumer<TaskProvider>(
actions: [ builder: (context, value, child) => CommonBackground(
IconButton( child: Scaffold(
onPressed: () { appBar: CommonAppBar(
Navigator.pop(context); // Go back to the previous screen actions: [
}, IconButton(
icon: Image.asset('assets/Back_attendance.png'), onPressed: () {
padding: const EdgeInsets.only(right: 20), 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 { class CalendarWidget extends StatefulWidget {
const CalendarWidget({super.key}); const CalendarWidget({super.key});
@ -56,117 +86,142 @@ class CalendarWidget extends StatefulWidget {
class _CalendarWidgetState extends State<CalendarWidget> { class _CalendarWidgetState extends State<CalendarWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( // Card UI to display the calendar inside return Card(
margin: const EdgeInsets.all(16), // Margin around the card margin: const EdgeInsets.all(16),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), // Padding inside the card padding: const EdgeInsets.all(16),
child: Column( child: TableCalendar(
crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left firstDay: DateTime.utc(1900, 5, 1),
children: [ lastDay: DateTime.utc(2900, 5, 1),
const Text( focusedDay: _focusedDay,
'Calendar', selectedDayPredicate: (day) {
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // Calendar heading return isSameDay(_selectedDay, day);
), },
const Text( onDaySelected: (selectedDay, focusedDay) {
'Month/Year', setState(() {
style: TextStyle(fontSize: 14, color: Colors.grey), // Display current month and year _selectedDay = selectedDay;
), _focusedDay = focusedDay;
const SizedBox(height: 10), context.read<TaskProvider>().getAllTaskByDate(_selectedDay);
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 onPageChanged: (focusedDay) {
focusedDay: _focusedDay, // Currently focused day _focusedDay = focusedDay;
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
},
),
],
), ),
), ),
); );
} }
} }
// Widget to display a list of events class TaskList extends StatefulWidget {
class EventsList extends StatelessWidget { final TaskProvider taskProvider;
const EventsList({super.key});
const TaskList({super.key, required this.taskProvider});
@override
State<TaskList> createState() => _TaskListState();
}
class _TaskListState extends State<TaskList> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.taskProvider.getAllTaskByDate(_selectedDay);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Card( // Card UI for the event list return (widget.taskProvider.taskModelList.isEmpty)
margin: EdgeInsets.all(16), // Margin around the card ? Container(
child: Padding( alignment: Alignment.center,
padding: EdgeInsets.all(16), // Padding inside the card child: const Text('No task found',
child: Column( style: TextStyle(fontSize: 20, color: Colors.white)),
crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left )
children: [ : Expanded(
Text( child: ListView.builder(
'Events List', // Heading for the events list itemCount: widget.taskProvider.taskModelList.length,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), itemBuilder: (context, index) {
return _taskView(
task: widget.taskProvider.taskModelList[index]);
},
), ),
SizedBox(height: 10), );
// Display individual event items }
EventItem(
date: '10-06-2024', Widget _customContainer({required Widget child}) {
event: 'Meeting with Territory Manager', return Padding(
), padding: const EdgeInsets.symmetric(horizontal: 16),
SizedBox(height: 10), child: Container(
EventItem( width: double.infinity,
date: '10-06-2024', padding: const EdgeInsets.all(12.0),
event: 'Sales Data Review', 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 Widget _taskView({required TaskModel task}) {
class EventItem extends StatelessWidget { final formattedDate = DateFormat('dd/MM/yyyy').format(task.taskDueDate);
final String date; // Event date return Column(
final String event; // Event description children: [
_customContainer(
const EventItem({super.key, required this.date, required this.event}); child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@override children: [
Widget build(BuildContext context) { Text(
return Container( "Assigned to: ${task.taskAssignedTo.name}",
padding: const EdgeInsets.all(12), // Padding inside the event container style: const TextStyle(
decoration: BoxDecoration( fontFamily: 'Anek',
color: Colors.blue[50], // Light blue background for the event item fontWeight: FontWeight.bold,
borderRadius: BorderRadius.circular(8), // Rounded corners for the container ),
), ),
child: Column( const SizedBox(height: 5),
crossAxisAlignment: CrossAxisAlignment.start, // Align items to the left Text(
children: [ "Task: ${task.task}",
Text('Date: $date'), // Display event date style: const TextStyle(
Text( fontFamily: 'Anek',
'Event: $event', // Display event description ),
style: const TextStyle(fontWeight: FontWeight.bold), ),
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 const SizedBox(height: 10),
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
),
],
),
); );
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:cheminova/notification_services.dart'; import 'package:cheminova/notification_services.dart';
import 'package:cheminova/provider/home_provider.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/Update_inventorytask_screen.dart';
import 'package:cheminova/screens/calendar_screen.dart'; import 'package:cheminova/screens/calendar_screen.dart';
import 'package:cheminova/screens/daily_tasks_screen.dart'; import 'package:cheminova/screens/daily_tasks_screen.dart';
@ -215,6 +216,20 @@ class _HomePageState extends State<HomePage> {
}, },
), ),
const SizedBox(height: 5), 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 containing cards for calendar and products manual
Row( Row(
children: [ children: [

View File

@ -6,22 +6,27 @@ class ApiUrls {
static const String getPdUrl = 'kyc/get-pd'; static const String getPdUrl = 'kyc/get-pd';
static const String createCollectKycUrl = '${baseUrl}kyc/create'; static const String createCollectKycUrl = '${baseUrl}kyc/create';
static const String getProfileUrl = '${baseUrl}salescoordinator/my-profile'; static const String getProfileUrl = '${baseUrl}salescoordinator/my-profile';
static const String changePasswordUrl = '${baseUrl}salescoordinator/password/update'; static const String changePasswordUrl =
static const String forgotPasswordUrl = '${baseUrl}salescoordinator/forgot-password'; '${baseUrl}salescoordinator/password/update';
static const String leaveAttendance = '${baseUrl}v1/markleave/salescoordinator'; 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 logOutUrl = '${baseUrl}salescoordinator/logout';
static const String rejectedApplication = '${baseUrl}kyc/getAllrejected'; static const String rejectedApplication = '${baseUrl}kyc/getAllrejected';
static const String notificationUrl = '${baseUrl}/get-notification-sc'; static const String notificationUrl = '${baseUrl}/get-notification-sc';
static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc'; static const String fcmUrl = '${baseUrl}kyc/save-fcm-sc';
static const String getProducts = '${baseUrl}product/getAll/user'; static const String getProducts = '${baseUrl}product/getAll/user';
static const String getPdRdUrl = '${baseUrl}inventory/distributors-SC/'; static const String getPdRdUrl = '${baseUrl}inventory/distributors-SC/';
static const String submitProductUrl = '${baseUrl}inventory/add-SC'; static const String submitProductUrl = '${baseUrl}inventory/add';
static const String selectTaskUrl = '${baseUrl}task/tasks'; static const String selectTaskUrl = '${baseUrl}task/tasks';
static const String dailyTaskUrl = '${baseUrl}task/tasks/'; static const String dailyTaskUrl = '${baseUrl}task/tasks/';
static const String kycSelectTaskUrl = '${baseUrl}task/task/type/Collect KYC'; static const String kycSelectTaskUrl = '${baseUrl}task/task/type/Collect KYC';
static const String updateTaskInventoryUrl = '${baseUrl}task/update-task-status/'; static const String updateTaskInventoryUrl = '${baseUrl}task/update-task-status/';
static const String getProductsManual = '${baseUrl}productmanual/getall'; static const String getProductsManual = '${baseUrl}productmanual/getall';
static const String salesTaskUrl = '${baseUrl}product/getAll/user/'; static const String salesTaskUrl = '${baseUrl}product/getAll/user/';
static const String postSalesTaskUrl = '${baseUrl}sales/add-SC'; static const String postSalesTaskUrl = '${baseUrl}sales/add-SC';
static const String submitVisitUrl = '${baseUrl}visit'; static const String submitVisitUrl = '${baseUrl}visit';
static const String allTaskByDate = '${baseUrl}task/alltask';
static const String announcementUrl = '${baseUrl}announcement/SCs';
} }