Compare commits

...

2 Commits

Author SHA1 Message Date
kratikpal
7d424edc11 Calendar API 2024-10-14 13:01:09 +05:30
kratikpal
5326f4bd86 Update sales data task 2024-09-11 16:56:08 +05:30
28 changed files with 1640 additions and 528 deletions

View File

@ -8,3 +8,5 @@ final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState> get scaffoldMessengerKey => GlobalKey<ScaffoldMessengerState> get scaffoldMessengerKey =>
_scaffoldMessengerKey; _scaffoldMessengerKey;
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

View File

@ -1,5 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/provider/add_sales_provider.dart';
import 'package:cheminova/provider/collect_kyc_provider.dart'; import 'package:cheminova/provider/collect_kyc_provider.dart';
import 'package:cheminova/provider/pd_rd_provider.dart'; import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/provider/product_manual_provider.dart'; import 'package:cheminova/provider/product_manual_provider.dart';
@ -106,6 +108,7 @@ Future<void> main() async {
ChangeNotifierProvider(create: (_) => PdRdProvider()), ChangeNotifierProvider(create: (_) => PdRdProvider()),
ChangeNotifierProvider(create: (_) => TaskProvider()), ChangeNotifierProvider(create: (_) => TaskProvider()),
ChangeNotifierProvider(create: (_) => ProductManualProvider()), ChangeNotifierProvider(create: (_) => ProductManualProvider()),
ChangeNotifierProvider(create: (_) => AddSalesProvider()),
], ],
child: const MyApp(), child: const MyApp(),
), ),
@ -123,6 +126,7 @@ class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
// scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey, // scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey,
title: 'cheminova', title: 'cheminova',

View File

@ -2,15 +2,16 @@ class PdRdResponseModel {
String? id; String? id;
String? uniqueId; String? uniqueId;
String? name; String? name;
String? tradeNameRd;
ShippingAddress? shippingAddress; ShippingAddress? shippingAddress;
Kyc? kyc;
String? salesCoordinator; // Nullable property for SalesCoordinator String? salesCoordinator; // Nullable property for SalesCoordinator
PdRdResponseModel({ PdRdResponseModel({
this.id, this.id,
this.uniqueId, this.uniqueId,
this.name, this.name,
this.tradeNameRd, this.kyc,
this.shippingAddress, this.shippingAddress,
this.salesCoordinator, // Initialize SalesCoordinator this.salesCoordinator, // Initialize SalesCoordinator
}); });
@ -20,7 +21,7 @@ class PdRdResponseModel {
id: json["_id"], id: json["_id"],
name: json["name"], name: json["name"],
uniqueId: json["uniqueId"], uniqueId: json["uniqueId"],
tradeNameRd: json["trade_name"], kyc: json["kyc"] != null ? Kyc.fromJson(json["kyc"]) : null,
shippingAddress: json['shippingAddress'] != null shippingAddress: json['shippingAddress'] != null
? ShippingAddress.fromJson(json['shippingAddress']) ? ShippingAddress.fromJson(json['shippingAddress'])
: null, : null,
@ -70,3 +71,53 @@ class ShippingAddress {
); );
} }
} }
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,174 @@
class SalesTaskResponse {
bool? success;
int? totalData;
int? totalPages;
List<SalesProduct>? products;
SalesTaskResponse(
{this.success, this.totalData, this.totalPages, this.products});
SalesTaskResponse.fromJson(Map<String, dynamic> json) {
success = json['success'];
totalData = json['total_data'];
totalPages = json['total_pages'];
if (json['products'] != null) {
products = <SalesProduct>[];
json['products'].forEach((v) {
products!.add(SalesProduct.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['success'] = success;
data['total_data'] = totalData;
data['total_pages'] = totalPages;
if (products != null) {
data['products'] = products!.map((v) => v.toJson()).toList();
}
return data;
}
}
class SalesProduct {
String? sId;
String? SKU;
String? ProductName;
Category? category;
Brand? brand;
int? price;
int? gST;
int? hSNCode;
String? description;
String? productStatus;
AddedBy? addedBy;
List<Null>? image;
String? createdAt;
String? updatedAt;
int? SalesAmount;
int? QuantitySold;
String? comments;
int? iV;
SalesProduct(
{this.sId,
this.SKU,
this.ProductName,
this.category,
this.brand,
this.price,
this.gST,
this.hSNCode,
this.description,
this.productStatus,
this.addedBy,
this.image,
this.createdAt,
this.updatedAt,
this.QuantitySold,
this.comments,
this.SalesAmount,
this.iV});
SalesProduct.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
SKU = json['SKU'];
ProductName = json['name'];
category =
json['category'] != null ? Category.fromJson(json['category']) : null;
brand = json['brand'] != null ? Brand.fromJson(json['brand']) : null;
price = json['price'];
gST = json['GST'];
hSNCode = json['HSN_Code'];
description = json['description'];
productStatus = json['product_Status'];
addedBy =
json['addedBy'] != null ? AddedBy.fromJson(json['addedBy']) : null;
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
iV = json['__v'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['SKU'] = SKU;
data['name'] = ProductName;
if (category != null) {
data['category'] = category!.toJson();
}
if (brand != null) {
data['brand'] = brand!.toJson();
}
data['price'] = price;
data['GST'] = gST;
data['HSN_Code'] = hSNCode;
data['description'] = description;
data['product_Status'] = productStatus;
if (addedBy != null) {
data['addedBy'] = addedBy!.toJson();
}
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
data['__v'] = iV;
return data;
}
}
class Category {
String? sId;
String? categoryName;
Category({this.sId, this.categoryName});
Category.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
categoryName = json['categoryName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['categoryName'] = categoryName;
return data;
}
}
class Brand {
String? sId;
String? brandName;
Brand({this.sId, this.brandName});
Brand.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
brandName = json['brandName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['brandName'] = brandName;
return data;
}
}
class AddedBy {
String? sId;
String? name;
AddedBy({this.sId, this.name});
AddedBy.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['name'] = name;
return data;
}
}

View File

@ -0,0 +1,105 @@
import 'dart:convert';
import 'package:cheminova/constants/constant.dart';
import 'package:cheminova/models/sales_task_response.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';
class AddSalesProvider with ChangeNotifier {
final _apiClient = ApiClient();
List<SalesProduct> tasksList = [];
List<SalesProduct> searchList = [];
bool _isLoading = false;
bool get isLoading => _isLoading;
List<SalesProduct> selectedProducts = [];
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
Future<void> getTask() async {
setLoading(true);
try {
Response response = await _apiClient.get(ApiUrls.salesTaskUrl);
setLoading(false);
if (response.statusCode == 200) {
final data = SalesTaskResponse.fromJson(response.data);
tasksList = data.products ?? [];
notifyListeners();
}
} catch (e) {
setLoading(false);
debugPrint("Error occurred while fetching sales tasks: $e");
}
}
void filterProducts(String val) {
tasksList = tasksList.where((element) {
final productNameLower = element.ProductName!.toLowerCase();
final productSkuLower = element.SKU!.toLowerCase();
final searchLower = val.toLowerCase();
return productNameLower.contains(searchLower) ||
productSkuLower.contains(searchLower);
}).toList();
notifyListeners();
}
Future<void> submitProducts(
{required String distributorType,
required String pdRdId,
required String date,
String? inventoryId,
required String tradeName}) async {
setLoading(true);
try {
Response response = await _apiClient.post(ApiUrls.postSalesTaskUrl,
data: json.encode({
"addedFor": distributorType.replaceAll(' ', ''),
"addedForId": pdRdId,
"tradename": tradeName,
"date": date,
"products": selectedProducts.map((product) {
return {
"SKU": product.SKU,
"ProductName": product.ProductName,
"QuantitySold": product.QuantitySold,
"comments": product.comments,
"SalesAmount": product.SalesAmount
};
}).toList()
}));
setLoading(false);
if (response.statusCode == 201) {
ScaffoldMessenger.of(
navigatorKey.currentContext!,
).showSnackBar(
SnackBar(content: Text(response.data['message'])),
);
resetProducts();
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull(),
),
);
}
} catch (e) {
setLoading(false);
debugPrint("Error: $e");
}
}
void resetProducts() {
selectedProducts.clear();
tasksList.clear();
notifyListeners();
}
}

View File

@ -30,6 +30,7 @@ class CollectKycProvider extends ChangeNotifier {
final aadharNumberController = TextEditingController(); final aadharNumberController = TextEditingController();
final panNumberController = TextEditingController(); final panNumberController = TextEditingController();
final gstNumberController = TextEditingController(); final gstNumberController = TextEditingController();
final emailController = TextEditingController();
String? selectedDistributor; String? selectedDistributor;
@ -210,6 +211,12 @@ class CollectKycProvider extends ChangeNotifier {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Selfie of Entrance Board is required')), const SnackBar(content: Text('Selfie of Entrance Board is required')),
); );
} else if (emailController.text.trim().isEmpty ||
!emailController.text.trim().contains(RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+"))) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Invalid email address')),
);
} else { } else {
submitCollectKycForm(context); submitCollectKycForm(context);
} }
@ -221,6 +228,7 @@ class CollectKycProvider extends ChangeNotifier {
'name': nameController.text.trim(), 'name': nameController.text.trim(),
'trade_name': tradeNameController.text.trim(), 'trade_name': tradeNameController.text.trim(),
'address': addressController.text.trim(), 'address': addressController.text.trim(),
'email': emailController.text.trim(),
'state': state.text.trim(), 'state': state.text.trim(),
'city': city.text.trim(), 'city': city.text.trim(),
'district': districtController.text.trim(), 'district': districtController.text.trim(),
@ -267,8 +275,13 @@ class CollectKycProvider extends ChangeNotifier {
} }
} else { } else {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: Text('Submission failed: ${response.statusMessage}'))); SnackBar(
content: Text(
'${response.data['message'] ?? 'Something went wrong'}',
),
),
);
} }
} }
} catch (e) { } catch (e) {

View File

@ -52,6 +52,7 @@ class ProductProvider extends ChangeNotifier {
"ProductName": product.name, "ProductName": product.name,
"Sale": product.sale, "Sale": product.sale,
"Inventory": product.inventory, "Inventory": product.inventory,
"productid": product.id,
}; };
}).toList(), }).toList(),
"addedFor": addedFor, "addedFor": addedFor,

View File

@ -112,7 +112,7 @@ class TaskProvider extends ChangeNotifier {
'addedForId': _selectedDistributor!.id, 'addedForId': _selectedDistributor!.id,
'tradename': selectedDistributorType == 'PrincipalDistributor' 'tradename': selectedDistributorType == 'PrincipalDistributor'
? _selectedDistributor!.shippingAddress!.tradeName ? _selectedDistributor!.shippingAddress!.tradeName
: _selectedDistributor!.tradeNameRd, : _selectedDistributor!.kyc!.tradeName,
}); });
} }
@ -180,7 +180,7 @@ class TaskProvider extends ChangeNotifier {
.map((json) => PdRdResponseModel.fromJson(json)) .map((json) => PdRdResponseModel.fromJson(json))
.toList(); .toList();
_rdList = data; _rdList = data;
print("RDTradeName ${data[0].tradeNameRd}"); print("RDTradeName ${data[0].kyc!.tradeName}");
} else { } else {
print("Failed to load data: ${response.statusCode}"); print("Failed to load data: ${response.statusCode}");
} }
@ -245,4 +245,24 @@ class TaskProvider extends ChangeNotifier {
setLoading(false); setLoading(false);
} }
} }
Future<void> 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<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

@ -1,32 +1,52 @@
import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../constants/constant.dart';
import '../screens/data_submit_successfull.dart';
import '../services/api_client.dart'; import '../services/api_client.dart';
import '../services/api_urls.dart'; import '../services/api_urls.dart';
class VisitPdRdProvider with ChangeNotifier { class VisitPdRdProvider with ChangeNotifier {
final _apiClient = ApiClient(); final _apiClient = ApiClient();
bool _isLoading = false; bool _isLoading = false;
bool get isLoading => _isLoading;
void setLoading(bool loading) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
notifyListeners(); notifyListeners();
} }
Future<void> submitVisitPdRd(BuildContext context, String id) async { bool get isLoading => _isLoading;
Future<void> 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); setLoading(true);
try { try {
Response response = Response response = await _apiClient.post(ApiUrls.visitRdPd, data: {
await _apiClient.put(ApiUrls.updateTaskInventoryUrl + id); "addedFor": distributorType,
debugPrint('Response: $response'); "addedForId": pdRdId,
"tradename": tradeName,
"visitDate": visitDate,
"visitTime": visitTime,
"visitpurpose": visitPurpose,
"meetingsummary": meetingSummary,
if (followUp != null) "followup": followUp,
if (nextVisitDate != null) "nextvisitdate": nextVisitDate
});
setLoading(false); setLoading(false);
if (response.statusCode == 200) { if (response.statusCode == 201) {
Navigator.push( Navigator.push(
context, navigatorKey.currentContext!,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull())); builder: (context) => const DataSubmitSuccessfull(),
),
);
} }
} catch (e) { } catch (e) {
setLoading(false); setLoading(false);

View File

@ -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<VisitDealersScreen> createState() => VisitDealersScreenState();
}
class VisitDealersScreenState extends State<VisitDealersScreen> {
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<String> purposeOptions = ['Sales', 'Dues collection', 'Others'];
Future<void> _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: <Widget>[
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: <Widget>[
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<String>(
decoration: const InputDecoration(
labelText: 'Purpose of visit',
fillColor: Colors.white,
filled: true,
),
value: selectedPurpose,
items: purposeOptions.map((String value) {
return DropdownMenuItem<String>(
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<VisitPdRdProvider>(
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 ?? '');
},
),
),
),
],
),
),
],
),
),
),
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/models/product_model.dart'; import 'package:cheminova/models/product_model.dart';
import 'package:cheminova/provider/product_provider.dart'; import 'package:cheminova/provider/product_provider.dart';
import 'package:cheminova/screens/data_submit_successfull.dart'; import 'package:cheminova/screens/data_submit_successfull.dart';
import 'package:cheminova/utils/string_extension.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
@ -72,7 +73,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
), ),
], ],
title: Text( title: Text(
widget.distributor.name!, widget.distributor.name!.capitalize(),
style: const TextStyle( style: const TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
@ -160,7 +161,8 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
child: ListTile( child: ListTile(
title: Text( title: Text(
filteredProducts[index] filteredProducts[index]
.name, .name
.capitalize(),
style: TextStyle( style: TextStyle(
color: isAlreadySelected color: isAlreadySelected
? Colors.grey ? Colors.grey
@ -221,7 +223,10 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
onPressed: () { onPressed: () {
provider provider
.submitSelectedProducts( .submitSelectedProducts(
"PrincipalDistributor", (widget.distributorType ==
'Principal Distributor')
? 'PrincipalDistributor'
: 'RetailDistributor',
widget.distributor.id!) widget.distributor.id!)
.then((value) { .then((value) {
if (value) { if (value) {
@ -232,6 +237,12 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
const DataSubmitSuccessfull(), const DataSubmitSuccessfull(),
), ),
); );
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Insufficient stock"),
),
);
} }
}); });
}, },
@ -277,15 +288,8 @@ class _ProductBlockState extends State<ProductBlock> {
inventoryController.text.isNotEmpty) { inventoryController.text.isNotEmpty) {
// Check if both inputs are valid integers // Check if both inputs are valid integers
try { try {
int sale = int.parse(saleController.text); int.parse(saleController.text);
int inventory = int.parse(inventoryController.text); 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;
}
} catch (e) { } catch (e) {
// Handle the case where input is not a valid integer // Handle the case where input is not a valid integer
errorMessage = 'Please enter valid integer values for both fields'; errorMessage = 'Please enter valid integer values for both fields';
@ -308,7 +312,8 @@ class _ProductBlockState extends State<ProductBlock> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Product: ${provider.selectedProducts[widget.index].name}', Text(
'Product: ${provider.selectedProducts[widget.index].name.capitalize()}',
style: const TextStyle(fontSize: 16)), style: const TextStyle(fontSize: 16)),
Text('SKU: ${provider.selectedProducts[widget.index].SKU}', Text('SKU: ${provider.selectedProducts[widget.index].SKU}',
style: const TextStyle(fontSize: 15)), style: const TextStyle(fontSize: 15)),

View File

@ -0,0 +1,463 @@
import 'package:cheminova/models/sales_task_response.dart';
import 'package:cheminova/provider/add_sales_provider.dart';
import 'package:cheminova/utils/string_extension.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class AddSalesProductScreen extends StatefulWidget {
final String distributorType;
final String tradeName;
final String pdRdId;
final String? inventoryId;
const AddSalesProductScreen(
{super.key,
required this.distributorType,
required this.tradeName,
required this.pdRdId,
this.inventoryId});
@override
State<AddSalesProductScreen> createState() => _AddSalesProductScreenState();
}
class _AddSalesProductScreenState extends State<AddSalesProductScreen> {
final searchController = TextEditingController();
late AddSalesProvider salesTaskProvider;
final dateController = TextEditingController();
final formKey = GlobalKey<FormState>();
@override
void initState() {
salesTaskProvider = Provider.of<AddSalesProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
salesTaskProvider.getTask();
});
super.initState();
}
@override
void dispose() {
if (mounted) {
salesTaskProvider.resetProducts();
}
super.dispose();
}
datePicker() async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2101),
);
if (picked != null) {
setState(
() => dateController.text = DateFormat('dd/MM/yyyy').format(picked));
}
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: true,
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: Text(
'${widget.distributorType}\n${widget.tradeName.capitalize()}',
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<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: [
FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height *
0.9),
context: context,
builder: (BuildContext context) {
return Consumer<AddSalesProvider>(
builder: (context, value, child) =>
StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
Padding(
padding:
const EdgeInsets.all(18.0),
child: TextField(
controller:
searchController,
decoration: const InputDecoration(
labelText:
'Search by Name or SKU',
border:
OutlineInputBorder(),
prefixIcon:
Icon(Icons.search)),
onChanged: (val) {
value.filterProducts(val);
setState(() {});
})),
Expanded(
child: ListView.builder(
itemCount: searchController
.text.isEmpty
? value.tasksList.length
: value.searchList.length,
itemBuilder: (context, index) {
bool isAlreadySelected = value
.selectedProducts
.any((selectedProduct) =>
selectedProduct.SKU ==
value.tasksList[index]
.SKU);
final data = searchController
.text.isEmpty
? value.tasksList[index]
: value.searchList[index];
return Card(
child: ListTile(
title: Text(
data.ProductName
?.capitalize() ??
'',
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors
.black)),
subtitle: Text(
data.SKU ?? '',
style: TextStyle(
color: isAlreadySelected
? Colors.grey
: Colors
.black)),
onTap: isAlreadySelected
? null
: () {
setState(() => value
.selectedProducts
.add(data));
Navigator.pop(
context);
},
),
);
},
),
)
],
);
},
),
);
},
).whenComplete(() => setState(() {}));
},
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),
Consumer<AddSalesProvider>(
builder: (context, value, child) =>
CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
if (formKey.currentState!.validate()) {
if (value.selectedProducts.isNotEmpty &&
value.selectedProducts.every((product) =>
product.SKU!.isNotEmpty &&
product.ProductName!.isNotEmpty &&
product.SalesAmount != null &&
product.QuantitySold != null)) {
value.submitProducts(
distributorType: widget.distributorType,
pdRdId: widget.pdRdId,
inventoryId: widget.inventoryId,
date: dateController.text.trim(),
tradeName: widget.tradeName);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Please fill out all product details, including sale and inventory.',
),
),
);
}
}
},
),
),
]
],
),
),
),
],
),
),
body: Consumer<AddSalesProvider>(
builder: (context, value, child) {
return Stack(
children: [
Column(
children: [
GestureDetector(
onTap: () => datePicker(),
child: AbsorbPointer(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: formKey,
child: TextFormField(
controller: dateController,
validator: (value) {
if (value!.isEmpty) {
return 'Please select a date';
}
return null;
},
decoration: const InputDecoration(
labelText: 'Date',
fillColor: Colors.white,
filled: true,
border: InputBorder.none,
suffixIcon: Icon(Icons.calendar_today),
),
),
),
),
),
),
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]);
},
),
)
],
),
(value.isLoading)
? Container(
color: Colors.black12,
child: const Center(
child: CircularProgressIndicator(),
),
)
: const SizedBox()
],
);
},
),
),
),
);
}
}
class ProductBlock extends StatefulWidget {
final SalesProduct product;
final ValueChanged<SalesProduct> onUpdate;
final VoidCallback onRemove;
const ProductBlock({
super.key,
required this.product,
required this.onUpdate,
required this.onRemove,
});
@override
State<ProductBlock> createState() => _ProductBlockState();
}
class _ProductBlockState extends State<ProductBlock> {
final saleAmountController = TextEditingController();
final quantitySoldController = TextEditingController();
final commentController = TextEditingController();
String? errorMessage;
@override
void initState() {
super.initState();
saleAmountController.text = (widget.product.SalesAmount ?? '').toString();
commentController.text = (widget.product.comments ?? '').toString();
quantitySoldController.text =
(widget.product.QuantitySold ?? '').toString();
}
void validateInput() {
setState(() {
String? quantitySoldError;
String? salesAmountError;
if (saleAmountController.text.isEmpty) {
quantitySoldError = 'Quantity sold cannot be empty.';
}
if (quantitySoldController.text.isEmpty) {
salesAmountError = 'Sales amount cannot be empty.';
}
errorMessage = null;
if (quantitySoldError == null && salesAmountError == null) {
int quantitySold = int.parse(quantitySoldController.text);
int salesAmount = int.parse(saleAmountController.text);
String comments = commentController.text;
widget.onUpdate(SalesProduct(
SKU: widget.product.SKU,
ProductName: widget.product.ProductName,
QuantitySold: quantitySold,
SalesAmount: salesAmount,
comments: comments,
));
} else {
errorMessage = quantitySoldError ?? salesAmountError;
}
});
}
@override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
margin: const EdgeInsets.all(8),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(16),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('Product: ${widget.product.ProductName!.capitalize()}',
style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.SKU}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8),
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
TextField(
controller: saleAmountController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Sales amount',
errorText: saleAmountController.text.isEmpty
? 'Sales amount cannot be empty.'
: null),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput()),
TextField(
controller: quantitySoldController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Quantity sold',
errorText: quantitySoldController.text.isEmpty
? 'Quantity sold cannot be empty.'
: null),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput()),
TextField(
controller: commentController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
decoration: const InputDecoration(
labelText: 'Comments',
),
enabled: true,
onChanged: (_) => validateInput())
]),
]),
),
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: const Icon(
Icons.delete_outlined,
color: Colors.red,
),
onPressed: widget.onRemove)),
],
),
);
}
}
class ProductModel {
final String sku;
final String productName;
final int? sale;
final int? inventory;
final String? comments;
final String? date;
ProductModel({
required this.sku,
required this.productName,
this.sale,
this.inventory,
this.comments,
this.date,
});
}

View File

@ -119,15 +119,15 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
), ),
), ),
), ),
const SizedBox(height: 15), // const SizedBox(height: 15),
CommonElevatedButton( // CommonElevatedButton(
backgroundColor: const Color(0xff004791), // backgroundColor: const Color(0xff004791),
borderRadius: 30, // borderRadius: 30,
width: double.infinity, // width: double.infinity,
height: kToolbarHeight - 10, // height: kToolbarHeight - 10,
text: 'MANAGE SCs', // text: 'MANAGE SCs',
onPressed: () {}, // onPressed: () {},
), // ),
], ],
), ),
), ),

View File

@ -1,6 +1,7 @@
import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/provider/task_provider.dart'; import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/screens/confirm_task_screen.dart'; import 'package:cheminova/screens/confirm_task_screen.dart';
import 'package:cheminova/utils/string_extension.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
@ -149,15 +150,6 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text(
'Assign Tasks',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 20), const SizedBox(height: 20),
SearchField( SearchField(
suggestionsDecoration: SuggestionDecoration( suggestionsDecoration: SuggestionDecoration(
@ -166,7 +158,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
bottomRight: Radius.circular(8), bottomRight: Radius.circular(8),
), ),
border: Border.all( border: Border.all(
color: Colors.grey.withOpacity(0.5), color: Colors.white.withOpacity(0.5),
), ),
), ),
suggestionItemDecoration: BoxDecoration( suggestionItemDecoration: BoxDecoration(
@ -216,7 +208,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
(e) => SearchFieldListItem( (e) => SearchFieldListItem(
e.name!, e.name!,
item: e, item: e,
child: Text(e.name!), child: Text(e.name!.capitalize()),
), ),
) )
.toList(), .toList(),
@ -266,7 +258,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
taskProvider.setSelectedTask(value); taskProvider.setSelectedTask(value);
}, },
title: const Text( title: const Text(
"Update Sales Data Data", "Update Sales Data",
), ),
), ),
), ),
@ -321,6 +313,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
), ),
child: TextFormField( child: TextFormField(
controller: taskProvider.noteController, controller: taskProvider.noteController,
textCapitalization: TextCapitalization.sentences,
expands: true, expands: true,
maxLines: null, maxLines: null,
minLines: null, minLines: null,
@ -345,11 +338,12 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
if (taskProvider.selectedTask == if (taskProvider.selectedTask ==
'Update Inventory Data' || 'Update Inventory Data' ||
taskProvider.selectedTask == 'Visit RD/PD' || taskProvider.selectedTask == 'Visit RD/PD' ||
taskProvider.selectedTask == 'Update Sales Data') ...{ taskProvider.selectedTask ==
'Update Sales Data') ...{
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 15.0, horizontal: 5.0,
vertical: 5, vertical: 15,
), ),
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
decoration: const InputDecoration( decoration: const InputDecoration(
@ -384,7 +378,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
if (selectedDistributorType != null) if (selectedDistributorType != null)
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 25), horizontal: 5.0, vertical: 15),
child: DropdownButtonFormField<PdRdResponseModel>( child: DropdownButtonFormField<PdRdResponseModel>(
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Select Distributor Name', labelText: 'Select Distributor Name',
@ -400,7 +394,15 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
.map((PdRdResponseModel distributor) { .map((PdRdResponseModel distributor) {
return DropdownMenuItem<PdRdResponseModel>( return DropdownMenuItem<PdRdResponseModel>(
value: distributor, value: distributor,
child: Text(distributor.name!), child: Text(
(selectedDistributorType ==
'PrincipalDistributor'
? distributor
.shippingAddress!.tradeName
.capitalize()
: distributor.kyc!.tradeName
.capitalize()),
),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (value) {

View File

@ -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: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'; import '../widgets/common_app_bar.dart';
import '../widgets/common_background.dart'; import '../widgets/common_background.dart';
import '../widgets/common_drawer.dart'; import '../widgets/common_drawer.dart';
DateTime _focusedDay = DateTime.now();
DateTime _focusedDay=DateTime.now(); DateTime _selectedDay = DateTime.now();
DateTime? _selectedDay=DateTime.now();
class CalendarScreen extends StatelessWidget { class CalendarScreen extends StatelessWidget {
const CalendarScreen({super.key}); const CalendarScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonBackground( final taskProvider = context.watch<TaskProvider>();
return CommonBackground(
child: Scaffold( child: Scaffold(
appBar: CommonAppBar( appBar: CommonAppBar(
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
icon: Image.asset('assets/Back_attendance.png'), icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
), ),
], ],
title: const Text('Calendar'), title: const Text('Calendar'),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
body: const SingleChildScrollView( body: Stack(
child: Column( children: [
children: [ const Column(
CalendarWidget(), children: [
EventsList(), 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<CalendarWidget> {
margin: const EdgeInsets.all(16), margin: const EdgeInsets.all(16),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: TableCalendar(
crossAxisAlignment: CrossAxisAlignment.start, 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), return isSameDay(_selectedDay, day);
), },
const Text( onDaySelected: (selectedDay, focusedDay) {
'Month/Year', setState(() {
style: TextStyle(fontSize: 14, color: Colors.grey), _selectedDay = selectedDay;
), _focusedDay = focusedDay;
const SizedBox(height: 10), context.read<TaskProvider>().getAllTaskByDate(_selectedDay);
TableCalendar( });
firstDay: DateTime.utc(1900, 5, 1), },
lastDay: DateTime.utc(2900, 5, 1), onPageChanged: (focusedDay) {
focusedDay: _focusedDay, _focusedDay = focusedDay;
selectedDayPredicate: (day) { },
return isSameDay(_selectedDay, day);
},
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
),
],
), ),
), ),
); );
} }
} }
class EventsList extends StatelessWidget { class TaskList extends StatefulWidget {
const EventsList({super.key}); const TaskList({super.key});
@override
State<TaskList> createState() => _TaskListState();
}
class _TaskListState extends State<TaskList> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<TaskProvider>().getAllTaskByDate(_selectedDay);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Card( final taskProvider = context.watch<TaskProvider>();
margin: EdgeInsets.all(16),
child: Padding( return (taskProvider.taskModelList.isEmpty)
padding: EdgeInsets.all(16), ? Container(
child: Column( alignment: Alignment.center,
crossAxisAlignment: CrossAxisAlignment.start, child: const Text('No task found',
children: [ style: TextStyle(fontSize: 20, color: Colors.white)),
Text( )
'Events List', : Expanded(
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), 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', Widget _customContainer({required Widget child}) {
), return Padding(
SizedBox(height: 10), padding: const EdgeInsets.symmetric(horizontal: 16),
EventItem( child: Container(
date: '10-06-2024', width: double.infinity,
event: 'Sales Data Review', 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'),
),
],
),
);
}
}

View File

@ -1,8 +1,8 @@
import 'package:cheminova/screens/collect_kyc_screen.dart'; import 'package:cheminova/screens/collect_kyc_screen.dart';
import 'package:cheminova/screens/update_inventory_screen.dart'; import 'package:cheminova/screens/select_distributer_screen.dart';
import 'package:cheminova/screens/display_sales_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:flutter/material.dart';
import 'package:cheminova/screens/visit_Dealers_screen.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/widgets/common_background.dart';
@ -48,12 +48,14 @@ class DailyTasksScreen extends StatelessWidget {
'Visit Dealers/Retailers', 'Visit Dealers/Retailers',
'', '',
onTap: () { onTap: () {
Navigator.push( // Navigator.push(
context, // context,
MaterialPageRoute( // MaterialPageRoute(
builder: (context) => const VisitDealersScreen(), // builder: (context) => const VisitRdPdScreen(
), // tradeName: 't',
); // ),
// ),
// );
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
@ -61,18 +63,41 @@ class DailyTasksScreen extends StatelessWidget {
'Update Sales Data', 'Update Sales Data',
'', '',
onTap: () { onTap: () {
Navigator.push(context,MaterialPageRoute(builder: (context) => const DisplaySalesScreen(),)); Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const DisplaySalesScreen(),
));
}, },
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
_buildCustomCard('Update Inventory Data', '',onTap: () { _buildCustomCard(
Navigator.push(context, MaterialPageRoute(builder: (context) => const UpdateInventoryScreen(),)); 'Update Inventory Data',
},), '',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SelectDistributerScreen(
task: "Add Products",
),
));
},
),
const SizedBox(height: 5), const SizedBox(height: 5),
_buildCustomCard('Collect KYC Documents', '',onTap:() { _buildCustomCard(
Navigator.push(context, MaterialPageRoute( 'Collect KYC Documents',
builder: (context) => const CollectKycScreen(),)); '',
}, ), onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CollectKycScreen(),
));
},
),
], ],
), ),
), ),
@ -84,7 +109,8 @@ class DailyTasksScreen extends StatelessWidget {
); );
} }
Widget _buildCustomCard(String title, String subtitle, {void Function()? onTap}) { Widget _buildCustomCard(String title, String subtitle,
{void Function()? onTap}) {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -103,9 +129,9 @@ class DailyTasksScreen extends StatelessWidget {
), ),
subtitle: subtitle.isNotEmpty subtitle: subtitle.isNotEmpty
? Text( ? Text(
subtitle, subtitle,
style: const TextStyle(color: Colors.white70, fontSize: 13), style: const TextStyle(color: Colors.white70, fontSize: 13),
) )
: null, : null,
onTap: onTap, onTap: onTap,
), ),

View File

@ -1,6 +1,6 @@
import 'package:cheminova/notification_service.dart'; import 'package:cheminova/notification_service.dart';
import 'package:cheminova/provider/user_provider.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/assign_task_dash_board_screen.dart';
import 'package:cheminova/screens/calendar_screen.dart'; import 'package:cheminova/screens/calendar_screen.dart';
import 'package:cheminova/screens/collect_kyc_screen.dart'; import 'package:cheminova/screens/collect_kyc_screen.dart';
@ -8,8 +8,7 @@ import 'package:cheminova/screens/mark_attendence_screen.dart';
import 'package:cheminova/screens/notification_screen.dart'; import 'package:cheminova/screens/notification_screen.dart';
import 'package:cheminova/screens/products_manual_screen.dart'; import 'package:cheminova/screens/products_manual_screen.dart';
import 'package:cheminova/screens/rejected_application_screen.dart'; import 'package:cheminova/screens/rejected_application_screen.dart';
import 'package:cheminova/screens/product_sales_data.dart'; import 'package:cheminova/screens/select_distributer_screen.dart';
import 'package:cheminova/screens/update_inventory_screen.dart';
import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:cheminova/screens/display_sales_screen.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -119,13 +118,13 @@ class _HomePageState extends State<HomePage> {
Row( Row(
children: [ children: [
Expanded( Expanded(
child: _buildCustomCard('Display\nSales data', child: _buildCustomCard('Collect \nKYC Data',
'Quickly display Sales\n', onTap: () { 'Scan and upload KYC Documents', onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const DisplaySalesScreen(), const CollectKycScreen(),
)); ));
}), }),
), ),
@ -139,7 +138,9 @@ class _HomePageState extends State<HomePage> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const UpdateInventoryScreen(), const SelectDistributerScreen(
task: "Update Inventory",
),
)); ));
}), }),
), ),
@ -152,14 +153,16 @@ class _HomePageState extends State<HomePage> {
children: [ children: [
Expanded( Expanded(
child: _buildCustomCard( child: _buildCustomCard(
'Visit RD/PD', 'Visit RD/PD\n\n',
'\n\n', '',
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const VisitDealersScreen(), const SelectDistributerScreen(
task: "Visit RD/PD",
),
), ),
); );
}, },
@ -169,33 +172,17 @@ class _HomePageState extends State<HomePage> {
width: 12, width: 12,
), ),
Expanded( Expanded(
child: _buildCustomCard( child: _buildCustomCard('Update\nSales Data\n', '',
'Product\nSales Data Visibility', '',
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const ProductSalesData(), const SelectDistributerScreen(
)); task: "Update Sales",
}), ),
), ),
], );
),
const SizedBox(
height: 5,
),
Row(
children: [
Expanded(
child: _buildCustomCard('Collect \nKYC Data',
'Scan and upload KYC Documents', onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const CollectKycScreen(),
));
}), }),
), ),
], ],
@ -271,10 +258,6 @@ class _HomePageState extends State<HomePage> {
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
TextButton(
onPressed: () => throw Exception(),
child: const Text("Throw Test Exception"),
),
], ],
), ),
), ),

View File

@ -94,81 +94,83 @@ class MyListView extends StatelessWidget {
RejectedApplicationResponse item = value.rejectedApplicationList[index]; RejectedApplicationResponse item = value.rejectedApplicationList[index];
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10),
child: ExpansionTile( child: ClipRRect(
collapsedBackgroundColor: Colors.white, borderRadius: BorderRadius.circular(15), // Rounded corner
backgroundColor: Colors.white, child: ExpansionTile(
title: Text( collapsedBackgroundColor: Colors.white,
item.tradeName ?? '', backgroundColor: Colors.white,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), title: Text(
), item.tradeName ?? '',
subtitle: Column( style:
crossAxisAlignment: CrossAxisAlignment.start, const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
children: [ ),
Text((DateFormat("dd MMMM yyyy") subtitle: Column(
.format(DateTime.parse(item.createdAt ?? '')))), crossAxisAlignment: CrossAxisAlignment.start,
Text(item.sId ?? ''), children: [
for (var note in item.notes!) Text(note.message ?? ''), Text((DateFormat("dd MMMM yyyy")
.format(DateTime.parse(item.createdAt ?? '')))),
Text("ID:${item.sId ?? ''}"),
for (var note in item.notes!) Text(note.message ?? ''),
],
),
children: <Widget>[
ListTile(
title: Text('Address: ${item.address ?? ''}'),
),
ListTile(
title: Text('City: ${item.city ?? ''}'),
),
ListTile(
title: Text('State: ${item.state ?? ''}'),
),
ListTile(
title: Text('Pincode: ${item.pincode ?? ''}'),
),
ListTile(
title: Text('Mobile: ${item.mobileNumber ?? ''}'),
),
ListTile(
title: Text('Status: ${item.status ?? ''}'),
),
ListTile(
title: Text(
'Principal Distributor: ${item.principalDistributer!.name ?? ''}'),
),
ListTile(
title: Text('PAN Number: ${item.panNumber ?? ''}'),
),
Image.network(item.panImg!.url ?? '', height: 250, width: 250),
ListTile(
title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'),
),
Image.network(item.aadharImg?.url ?? '',
height: 250, width: 250),
ListTile(
title: Text('GST Number: ${item.gstNumber ?? ''}'),
),
Image.network(item.gstImg!.url ?? '', height: 250, width: 250),
const ListTile(
title: Text('Pesticide License: '),
),
if (item.pesticideLicenseImg != null)
Image.network(item.pesticideLicenseImg!.url ?? '',
height: 250, width: 250),
const ListTile(
title: Text('selfieEntranceImg: '),
),
Image.network(item.selfieEntranceImg!.url ?? '',
height: 250, width: 250),
const ListTile(
title: Text('Notes:'),
),
if (item.notes != null)
for (var note in item.notes!)
ListTile(
contentPadding: const EdgeInsets.only(left: 20),
title: Text(note.message ?? ''),
),
], ],
), ),
children: <Widget>[
ListTile(
title: Text('Address: ${item.address ?? ''}'),
),
ListTile(
title: Text('City: ${item.city ?? ''}'),
),
ListTile(
title: Text('State: ${item.state ?? ''}'),
),
ListTile(
title: Text('Pincode: ${item.pincode ?? ''}'),
),
ListTile(
title: Text('Mobile: ${item.mobileNumber ?? ''}'),
),
ListTile(
title: Text('Status: ${item.status ?? ''}'),
),
ListTile(
title: Text(
'Principal Distributor: ${item.principalDistributer!.name ?? ''}'),
),
ListTile(
title: Text('PAN Number: ${item.panNumber ?? ''}'),
),
Image.network(item.panImg!.url ?? '', height: 250, width: 250),
ListTile(
title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'),
),
Image.network(item.aadharImg?.url ?? '', height: 250, width: 250),
ListTile(
title: Text('GST Number: ${item.gstNumber ?? ''}'),
),
Image.network(item.gstImg!.url ?? '', height: 250, width: 250),
const ListTile(
title: Text('Pesticide License: '),
),
if (item.pesticideLicenseImg != null)
Image.network(item.pesticideLicenseImg!.url ?? '',
height: 250, width: 250),
// if (item['fertilizer_license_img'] != null)
// Image.network(item['fertilizer_license_img']['url'] ?? ''),
const ListTile(
title: Text('selfieEntranceImg: '),
),
Image.network(item.selfieEntranceImg!.url ?? '',
height: 250, width: 250),
const ListTile(
title: Text('Notes:'),
),
if (item.notes != null)
for (var note in item.notes!)
ListTile(
contentPadding: const EdgeInsets.only(left: 20),
title: Text(note.message ?? ''),
),
],
), ),
); );
}, },

View File

@ -1,11 +1,12 @@
import 'package:cheminova/models/get_pd_response.dart'; import 'package:cheminova/models/get_pd_response.dart';
import 'package:cheminova/provider/collect_kyc_provider.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:csc_picker/csc_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.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_elevated_button.dart';
import '../widgets/common_text_form_field.dart'; import '../widgets/common_text_form_field.dart';
@ -45,6 +46,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
children: <Widget>[ children: <Widget>[
CommonTextFormField( CommonTextFormField(
title: 'Trade Name', title: 'Trade Name',
textCapitalization: TextCapitalization.words,
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
@ -56,6 +58,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Name', title: 'Name',
textCapitalization: TextCapitalization.words,
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { validator: (String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
@ -65,6 +68,21 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
}, },
controller: value.nameController), controller: value.nameController),
const SizedBox(height: 15), const SizedBox(height: 15),
CommonTextFormField(
// maxLength: 20,
title: 'Email',
fillColor: Colors.white,
inputFormatters: [
NoSpaceFormatter(),
],
validator: (String? value) {
if (value!.isEmpty) {
return 'Email cannot be empty';
}
return null;
},
controller: value.emailController),
const SizedBox(height: 15),
CommonTextFormField( CommonTextFormField(
title: 'Address', title: 'Address',
fillColor: Colors.white, fillColor: Colors.white,
@ -110,6 +128,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
CommonTextFormField( CommonTextFormField(
maxLength: 6, maxLength: 6,
title: 'Pincode', title: 'Pincode',
keyboardType: TextInputType.number,
fillColor: Colors.white, fillColor: Colors.white,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly
@ -125,6 +144,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
CommonTextFormField( CommonTextFormField(
maxLength: 10, maxLength: 10,
title: 'Mobile Number', title: 'Mobile Number',
keyboardType: TextInputType.number,
fillColor: Colors.white, fillColor: Colors.white,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly
@ -140,6 +160,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
CommonTextFormField( CommonTextFormField(
maxLength: 12, maxLength: 12,
title: 'Aadhar Number', title: 'Aadhar Number',
keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly
], ],

View File

@ -1,6 +1,9 @@
import 'package:cheminova/models/pd_rd_response_model.dart'; import 'package:cheminova/models/pd_rd_response_model.dart';
import 'package:cheminova/provider/pd_rd_provider.dart'; import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/screens/add_products_screen.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_background.dart';
import 'package:cheminova/widgets/common_app_bar.dart'; import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_drawer.dart'; import 'package:cheminova/widgets/common_drawer.dart';
@ -8,14 +11,16 @@ import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class UpdateInventoryScreen extends StatefulWidget { class SelectDistributerScreen extends StatefulWidget {
const UpdateInventoryScreen({super.key}); final String task;
const SelectDistributerScreen({super.key, required this.task});
@override @override
State<UpdateInventoryScreen> createState() => _UpdateInventoryScreenState(); State<SelectDistributerScreen> createState() =>
_SelectDistributerScreenState();
} }
class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> { class _SelectDistributerScreenState extends State<SelectDistributerScreen> {
String? selectedDistributorType; String? selectedDistributorType;
PdRdResponseModel? selectedDistributor; PdRdResponseModel? selectedDistributor;
@ -45,7 +50,7 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
), ),
], ],
title: const Text( title: const Text(
'Update Inventory Data', 'Select Distributor',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
@ -69,18 +74,47 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
selectedDistributorType == null) { selectedDistributorType == null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text( content:
'Please select distributor type and distributor')), Text('Please select distributor type and distributor'),
),
); );
} else { } else {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (context) {
builder: (context) => AddProductsScreen( if (widget.task == 'Update Inventory') {
distributor: selectedDistributor!, return AddProductsScreen(
distributorType: selectedDistributorType!, distributor: selectedDistributor!,
), distributorType: selectedDistributorType!,
), );
} else if (widget.task == 'Update Sales') {
return AddSalesProductScreen(
distributorType:
selectedDistributorType == 'Principal Distributor'
? 'PrincipalDistributor'
: 'RetailDistributor',
tradeName: selectedDistributorType ==
'Principal Distributor'
? selectedDistributor!.shippingAddress!.tradeName
: 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();
}
}),
); );
} }
}, },
@ -143,7 +177,13 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
.map((PdRdResponseModel distributor) { .map((PdRdResponseModel distributor) {
return DropdownMenuItem<PdRdResponseModel>( return DropdownMenuItem<PdRdResponseModel>(
value: distributor, value: distributor,
child: Text(distributor.name!), child: Text(
selectedDistributorType ==
'PrincipalDistributor'
? distributor.shippingAddress!.tradeName
.capitalize()
: distributor.kyc!.tradeName.capitalize(),
),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (value) {

View File

@ -198,7 +198,7 @@ class _TaskManagementScreenState extends State<TaskManagementScreen> {
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.white), border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6), color: const Color(0xffB4D1E5).withOpacity(0.8),
borderRadius: BorderRadius.circular(16.0), borderRadius: BorderRadius.circular(16.0),
), ),
child: child, child: child,
@ -220,21 +220,38 @@ class _TaskManagementScreenState extends State<TaskManagementScreen> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 5),
Text( Text(
"Task: ${task.task}", "Task: ${task.task}",
style: const TextStyle( style: const TextStyle(
fontFamily: 'Anek', fontFamily: 'Anek',
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 5),
if (task.addedFor != null) ...{
task.addedFor == 'PrincipalDistributor'
? Text(
"PD: ${task.tradename}",
)
: Text("RD: ${task.tradename}"),
const SizedBox(height: 5),
},
Text( Text(
'Status: ${task.taskStatus}', 'Status: ${task.taskStatus}',
style: const TextStyle( style: const TextStyle(
fontFamily: 'Anek', fontFamily: 'Anek',
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 5),
if (task.note != null) ...{
Text(
'Note: ${task.note}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 5),
},
Text( Text(
'Deadline:$formatedDate', 'Deadline:$formatedDate',
style: const TextStyle( style: const TextStyle(

View File

@ -28,6 +28,7 @@ class VerifyDocumentsScreen extends StatelessWidget {
{ {
'Trade Name': value.tradeNameController.text, 'Trade Name': value.tradeNameController.text,
'Name': value.nameController.text, 'Name': value.nameController.text,
'Email': value.emailController.text,
'Address': value.addressController.text, 'Address': value.addressController.text,
'Town/City': value.city.text, 'Town/City': value.city.text,
'District': value.districtController.text, 'District': value.districtController.text,

View File

@ -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<VisitRdPdScreen> createState() => VisitRdPdScreenState();
}
class VisitRdPdScreenState extends State<VisitRdPdScreen> {
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<String> purposeOptions = ['Sales', 'Dues collection', 'Others'];
Future<void> _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: <Widget>[
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<void> _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: <Widget>[
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: <Widget>[
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<String>(
decoration: const InputDecoration(
labelText: 'Purpose of visit',
fillColor: Colors.white,
filled: true,
),
value: selectedPurpose,
items: purposeOptions.map((String value) {
return DropdownMenuItem<String>(
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<VisitPdRdProvider>(
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<VisitPdRdProvider>(builder: (context, value, child) {
if (value.isLoading) {
return Container(
color: Colors.black.withOpacity(0.5),
child: const Center(
child: CircularProgressIndicator(),
),
);
}
return const SizedBox.shrink();
}),
],
),
),
);
}
}

View File

@ -1,5 +1,6 @@
class ApiUrls { class ApiUrls {
static const String baseUrl = 'https://cheminova-api-2.onrender.com/api/'; // static const String baseUrl = 'https://cheminova-api-2.onrender.com/api/';
static const String baseUrl = 'https://api.cnapp.co.in/api/';
static const String loginUrl = 'territorymanager/login'; static const String loginUrl = 'territorymanager/login';
static const String profileUrl = 'territorymanager/my-profile'; static const String profileUrl = 'territorymanager/my-profile';
static const String markAttendanceUrl = 'v1/markattendance/territorymanager'; static const String markAttendanceUrl = 'v1/markattendance/territorymanager';
@ -14,10 +15,13 @@ class ApiUrls {
static const String getProducts = '${baseUrl}product/getAll/user/'; static const String getProducts = '${baseUrl}product/getAll/user/';
static const String getRd = 'inventory/distributors-TM/RetailDistributor'; static const String getRd = 'inventory/distributors-TM/RetailDistributor';
static const String getPd = 'inventory/distributors-TM/PrincipalDistributor'; 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 getSalesCoordinators = 'salescoordinator/getAll-TM';
static const String assignTask = 'task/assign-task'; static const String assignTask = 'task/assign-task';
static const String getProductsManual = 'productmanual/getall'; static const String getProductsManual = 'productmanual/getall';
static const String getAllTasks = 'task/alltasks/'; static const String getAllTasks = 'task/alltasks/';
static const String updateTaskInventoryUrl = ''; static const String salesTaskUrl = '${baseUrl}product/getAll/user/';
static const String postSalesTaskUrl = '${baseUrl}sales/add-TM';
static const String visitRdPd = '$baseUrl/visit';
static const String allTaskByDate = '${baseUrl}task/alltask';
} }

View File

@ -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,
);
}
}

View File

@ -0,0 +1,5 @@
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
class CommonTextFormField extends StatelessWidget { class CommonTextFormField extends StatelessWidget {
final String title; final String title;
final TextEditingController? controller; final TextEditingController? controller;
final TextCapitalization textCapitalization;
final String? Function(String?)? validator; final String? Function(String?)? validator;
final Color? fillColor; final Color? fillColor;
final bool? readOnly; final bool? readOnly;
@ -28,6 +29,7 @@ class CommonTextFormField extends StatelessWidget {
this.inputFormatters, this.inputFormatters,
this.maxLength, this.maxLength,
this.onChanged, this.onChanged,
this.textCapitalization = TextCapitalization.sentences,
this.obscureText = false, this.obscureText = false,
}); });
@ -44,6 +46,7 @@ class CommonTextFormField extends StatelessWidget {
// fontFamily: 'Anek')), // fontFamily: 'Anek')),
TextFormField( TextFormField(
controller: controller, controller: controller,
textCapitalization: textCapitalization,
readOnly: readOnly ?? false, readOnly: readOnly ?? false,
maxLines: maxLines, maxLines: maxLines,
maxLength: maxLength, maxLength: maxLength,