assign task of KYC

This commit is contained in:
kratikpal 2024-08-23 09:11:23 +05:30
parent 7cce7db73c
commit 1436aa0131
16 changed files with 1243 additions and 479 deletions

View File

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:cheminova/provider/collect_kyc_provider.dart';
import 'package:cheminova/provider/pd_rd_provider.dart';
import 'package:cheminova/provider/product_provider.dart';
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/provider/user_provider.dart';
import 'package:cheminova/screens/splash_screen.dart';
import 'package:firebase_core/firebase_core.dart';
@ -100,6 +101,7 @@ Future<void> main() async {
ChangeNotifierProvider(create: (_) => UserProvider()),
ChangeNotifierProvider(create: (_) => ProductProvider()),
ChangeNotifierProvider(create: (_) => PdRdProvider()),
ChangeNotifierProvider(create: (_) => TaskProvider()),
],
child: const MyApp(),
),
@ -121,8 +123,12 @@ class _MyAppState extends State<MyApp> {
// scaffoldMessengerKey: SnackBarService().scaffoldMessengerKey,
title: 'cheminova',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true),
home: const SplashScreen());
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xff004791),
),
useMaterial3: true,
),
home: const SplashScreen(),
);
}
}

View File

@ -1,123 +1,127 @@
class PdRdResponseModel {
String id;
String name;
String tradeName;
String address;
String state;
String city;
String district;
String pincode;
String mobileNumber;
String principalDistributer;
String panNumber;
ImageModel panImg;
String aadharNumber;
ImageModel aadharImg;
String gstNumber;
ImageModel gstImg;
ImageModel pesticideLicenseImg;
ImageModel selfieEntranceImg;
String status;
String addedBy;
String? id;
String? uniqueId;
String? name;
String? tradeName;
String? address;
String? state;
String? city;
String? district;
String? pincode;
String? mobileNumber;
String? principalDistributer;
String? panNumber;
ImageModel? panImg;
String? aadharNumber;
ImageModel? aadharImg;
String? gstNumber;
ImageModel? gstImg;
ImageModel? pesticideLicenseImg;
ImageModel? selfieEntranceImg;
String? status;
String? addedBy;
String? userType;
List<Note> notes;
DateTime createdAt;
DateTime updatedAt;
int v;
List<Note>? notes;
DateTime? createdAt;
DateTime? updatedAt;
int? v;
PdRdResponseModel({
required this.id,
required this.name,
required this.tradeName,
required this.address,
required this.state,
required this.city,
required this.district,
required this.pincode,
required this.mobileNumber,
required this.principalDistributer,
required this.panNumber,
required this.panImg,
required this.aadharNumber,
required this.aadharImg,
required this.gstNumber,
required this.gstImg,
required this.pesticideLicenseImg,
required this.selfieEntranceImg,
required this.status,
required this.addedBy,
this.id,
this.uniqueId,
this.name,
this.tradeName,
this.address,
this.state,
this.city,
this.district,
this.pincode,
this.mobileNumber,
this.principalDistributer,
this.panNumber,
this.panImg,
this.aadharNumber,
this.aadharImg,
this.gstNumber,
this.gstImg,
this.pesticideLicenseImg,
this.selfieEntranceImg,
this.status,
this.addedBy,
this.userType,
required this.notes,
required this.createdAt,
required this.updatedAt,
required this.v,
this.notes,
this.createdAt,
this.updatedAt,
this.v,
});
factory PdRdResponseModel.fromJson(Map<String, dynamic> json) =>
PdRdResponseModel(
id: json["_id"],
name: json["name"],
tradeName: json["trade_name"],
address: json["address"],
state: json["state"],
city: json["city"],
district: json["district"],
pincode: json["pincode"],
mobileNumber: json["mobile_number"],
principalDistributer: json["principal_distributer"],
panNumber: json["pan_number"],
panImg: ImageModel.fromJson(json["pan_img"]),
aadharNumber: json["aadhar_number"],
aadharImg: ImageModel.fromJson(json["aadhar_img"]),
gstNumber: json["gst_number"],
gstImg: ImageModel.fromJson(json["gst_img"]),
pesticideLicenseImg: ImageModel.fromJson(json["pesticide_license_img"]),
selfieEntranceImg: ImageModel.fromJson(json["selfie_entrance_img"]),
status: json["status"],
addedBy: json["addedBy"],
userType: json["userType"],
notes: List<Note>.from(json["notes"].map((x) => Note.fromJson(x))),
createdAt: DateTime.parse(json["createdAt"]),
updatedAt: DateTime.parse(json["updatedAt"]),
v: json["__v"],
uniqueId: json["uniqueId"],
// tradeName: json["trade_name"],
// address: json["address"],
// state: json["state"],
// city: json["city"],
// district: json["district"],
// pincode: json["pincode"],
// mobileNumber: json["mobile_number"],
// principalDistributer: json["principal_distributer"],
// panNumber: json["pan_number"],
// panImg: ImageModel.fromJson(json["pan_img"]),
// aadharNumber: json["aadhar_number"],
// aadharImg: ImageModel.fromJson(json["aadhar_img"]),
// gstNumber: json["gst_number"],
// gstImg: ImageModel.fromJson(json["gst_img"]),
// pesticideLicenseImg: ImageModel.fromJson(json["pesticide_license_img"]),
// selfieEntranceImg: ImageModel.fromJson(json["selfie_entrance_img"]),
// status: json["status"],
// addedBy: json["addedBy"],
// userType: json["userType"],
// notes: List<Note>.from(json["notes"].map((x) => Note.fromJson(x))),
// createdAt: DateTime.parse(json["createdAt"]),
// updatedAt: DateTime.parse(json["updatedAt"]),
// v: json["__v"],
);
Map<String, dynamic> toJson() => {
"_id": id,
"name": name,
"trade_name": tradeName,
"address": address,
"state": state,
"city": city,
"district": district,
"pincode": pincode,
"mobile_number": mobileNumber,
"principal_distributer": principalDistributer,
"pan_number": panNumber,
"pan_img": panImg.toJson(),
"aadhar_number": aadharNumber,
"aadhar_img": aadharImg.toJson(),
"gst_number": gstNumber,
"gst_img": gstImg.toJson(),
"pesticide_license_img": pesticideLicenseImg.toJson(),
"selfie_entrance_img": selfieEntranceImg.toJson(),
"status": status,
"addedBy": addedBy,
"userType": userType,
"notes": List<dynamic>.from(notes.map((x) => x.toJson())),
"createdAt": createdAt.toIso8601String(),
"updatedAt": updatedAt.toIso8601String(),
"__v": v,
"uniqueId": uniqueId,
// "trade_name": tradeName,
// "address": address,
// "state": state,
// "city": city,
// "district": district,
// "pincode": pincode,
// "mobile_number": mobileNumber,
// "principal_distributer": principalDistributer,
// "pan_number": panNumber,
// "pan_img": panImg.toJson(),
// "aadhar_number": aadharNumber,
// "aadhar_img": aadharImg.toJson(),
// "gst_number": gstNumber,
// "gst_img": gstImg.toJson(),
// "pesticide_license_img": pesticideLicenseImg.toJson(),
// "selfie_entrance_img": selfieEntranceImg.toJson(),
// "status": status,
// "addedBy": addedBy,
// "userType": userType,
// "notes": List<dynamic>.from(notes.map((x) => x.toJson())),
// "createdAt": createdAt.toIso8601String(),
// "updatedAt": updatedAt.toIso8601String(),
// "__v": v,
};
}
class ImageModel {
String publicId;
String url;
String? publicId;
String? url;
ImageModel({
required this.publicId,
required this.url,
this.publicId,
this.url,
});
factory ImageModel.fromJson(Map<String, dynamic> json) => ImageModel(
@ -132,14 +136,14 @@ class ImageModel {
}
class Note {
String message;
DateTime replyDate;
String id;
String? message;
DateTime? replyDate;
String? id;
Note({
required this.message,
required this.replyDate,
required this.id,
this.message,
this.replyDate,
this.id,
});
factory Note.fromJson(Map<String, dynamic> json) => Note(
@ -150,7 +154,7 @@ class Note {
Map<String, dynamic> toJson() => {
"message": message,
"replyDate": replyDate.toIso8601String(),
// "replyDate": replyDate.toIso8601String(),
"_id": id,
};
}

View File

@ -8,8 +8,10 @@ class PdRdProvider extends ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
List<PdRdResponseModel> _pdRdList = [];
List<PdRdResponseModel> get pdRdList => _pdRdList;
List<PdRdResponseModel> _pdList = [];
List<PdRdResponseModel> get pdList => _pdList;
List<PdRdResponseModel> _rdList = [];
List<PdRdResponseModel> get rdList => _rdList;
final _apiClient = ApiClient();
@ -18,16 +20,42 @@ class PdRdProvider extends ChangeNotifier {
notifyListeners();
}
Future<void> getPdRd() async {
void clearLists() {
_pdList.clear();
_rdList.clear();
notifyListeners();
}
Future<void> getPd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getPdRdUrl);
Response response = await _apiClient.get(ApiUrls.getPd);
if (response.statusCode == 200) {
// Assuming the response data is a list of PdRdResponseModel objects
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_pdRdList = data;
_pdList = data;
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
Future<void> getRd() async {
setLoading(true);
clearLists(); // Clear the list before fetching new data
try {
Response response = await _apiClient.get(ApiUrls.getRd);
if (response.statusCode == 200) {
List<PdRdResponseModel> data = (response.data as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_rdList = data;
} else {
print("Failed to load data: ${response.statusCode}");
}

View File

@ -0,0 +1,111 @@
import 'package:cheminova/models/pd_rd_response_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 = [];
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;
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
void setSelectedSalesCoordinator(PdRdResponseModel coordinator) {
_selectedSalesCoordinator = coordinator;
notifyListeners();
}
void setSelectedTask(String? task) {
_selectedTask = task;
notifyListeners();
}
void setSelectedPriority(String priority) {
_selectedPriority = priority;
notifyListeners();
}
void setDate(String date) {
_selectedDate = date;
notifyListeners();
}
void clear(){
_selectedSalesCoordinator = null;
_selectedTask = null;
_selectedPriority = null;
_selectedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
_noteController.clear();
notifyListeners();
}
Future<void> getSalesCoordinators() async {
setLoading(true);
try {
Response response = await _apiClient.get(ApiUrls.getSalesCoordinators);
if (response.statusCode == 200) {
List<PdRdResponseModel> data =
(response.data['salesCoOrinators'] as List)
.map((json) => PdRdResponseModel.fromJson(json))
.toList();
_salesCoordinators = data;
} else {
print("Failed to load data: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
setLoading(false);
} finally {
setLoading(false);
}
}
Future<void> assignTask(BuildContext context) async {
setLoading(true);
try {
Response response = await _apiClient.post(
ApiUrls.assignTask,
data: {
'taskAssignedTo': _selectedSalesCoordinator!.id,
'task': _selectedTask,
'taskPriority': _selectedPriority,
'taskDueDate': _selectedDate,
'note': _noteController.text,
},
);
if (response.statusCode == 201) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DataSubmitSuccessfull(),
),
);
} else {
print("Failed to assign task: ${response.statusCode}");
}
} catch (e) {
print("Error occurred: $e");
} finally {
setLoading(false);
}
}
}

View File

@ -72,7 +72,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
),
],
title: Text(
widget.distributor.name,
widget.distributor.name!,
style: const TextStyle(
fontSize: 20,
color: Colors.black,
@ -222,7 +222,7 @@ class _AddProductsScreenState extends State<AddProductsScreen> {
provider
.submitSelectedProducts(
"PrincipalDistributor",
widget.distributor.id)
widget.distributor.id!)
.then((value) {
if (value) {
Navigator.push(
@ -275,27 +275,25 @@ class _ProductBlockState extends State<ProductBlock> {
setState(() {
if (saleController.text.isNotEmpty &&
inventoryController.text.isNotEmpty) {
// Check if the input can be parsed as an integer
if (int.tryParse(saleController.text) == null ||
int.tryParse(inventoryController.text) == null) {
errorMessage = 'Please enter valid integer value';
} else {
// Parse the input as integers
// Check if both inputs are valid integers
try {
int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text);
// Validate if inventory is less than or equal to sales
// 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) {
// Handle the case where input is not a valid integer
errorMessage = 'Please enter valid integer values for both fields';
}
} else {
errorMessage = 'Please fill in both fields';
errorMessage = null;
}
});
return errorMessage == null;
}

View File

@ -1,4 +1,5 @@
import 'package:cheminova/screens/select_sales_coordinator_screen.dart';
import 'package:cheminova/screens/assign_tasks_screen.dart';
import 'package:cheminova/screens/task_management_screen.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
@ -55,39 +56,40 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Assign Tasks',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child:
_customCard(title: "Total Tasks", subtitle: "100"),
),
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child: _customCard(
title: "Tasks Pending", subtitle: "100"),
),
SizedBox(
height: 200,
width: MediaQuery.of(context).size.width / 4.2,
child: _customCard(
title: "Reports Submitted", subtitle: "100"),
),
],
),
// const Text(
// 'Assign Tasks',
// style: TextStyle(
// fontSize: 24,
// color: Colors.white,
// fontWeight: FontWeight.bold,
// fontFamily: 'Anek',
// ),
// ),
// const SizedBox(height: 10),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child:
// _customCard(title: "Total Tasks", subtitle: "100"),
// ),
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child: _customCard(
// title: "Tasks Pending", subtitle: "100"),
// ),
// SizedBox(
// height: 200,
// width: MediaQuery.of(context).size.width / 4.2,
// child: _customCard(
// title: "Reports Submitted", subtitle: "100"),
// ),
// ],
// ),
const SizedBox(height: 20),
CommonElevatedButton(
backgroundColor: const Color(0xff004791),
borderRadius: 30,
@ -98,8 +100,7 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SelectSalesCoordinatorScreen(),
builder: (context) => const AssignTasksScreen(),
),
);
},
@ -111,7 +112,12 @@ class _AssignTaskDashBoardScreenState extends State<AssignTaskDashBoardScreen> {
width: double.infinity,
height: kToolbarHeight - 10,
text: 'VIEW TASK STATUS',
onPressed: () {},
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TaskManagementScreen(),
),
),
),
const SizedBox(height: 15),
CommonElevatedButton(

View File

@ -1,9 +1,13 @@
import 'package:cheminova/provider/task_provider.dart';
import 'package:cheminova/screens/confirm_task_screen.dart';
import 'package:provider/provider.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:intl/intl.dart';
import 'package:searchfield/searchfield.dart';
class AssignTasksScreen extends StatefulWidget {
const AssignTasksScreen({super.key});
@ -13,11 +17,45 @@ class AssignTasksScreen extends StatefulWidget {
}
class _AssignTasksScreenState extends State<AssignTasksScreen> {
final List<bool> _isChecked = List.generate(8, (_) => false);
bool _isSalesCoordinatorValid = true;
bool _isTaskValid = true;
bool _isPriorityValid = true;
bool _isNotesValid = true;
String _dateSelected = DateFormat('dd/MM/yyyy').format(DateTime.now());
void _validateAndSubmit() {
final taskProvider = context.read<TaskProvider>();
setState(() {
// Validate Sales Coordinator
_isSalesCoordinatorValid = taskProvider.selectedSalesCoordinator != null;
// Validate Task
_isTaskValid = taskProvider.selectedTask != null &&
taskProvider.selectedTask!.isNotEmpty;
// Validate Priority
_isPriorityValid = taskProvider.selectedPriority != null &&
taskProvider.selectedPriority!.isNotEmpty;
// Validate Notes
if (taskProvider.selectedTask == 'Collect KYC' &&
taskProvider.noteController.text.isEmpty) {
_isNotesValid = false;
}
});
// If all fields are valid, proceed to the next screen
if (_isSalesCoordinatorValid && _isTaskValid && _isPriorityValid) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ConfirmTaskScreen(),
),
);
}
}
Future<void> _selectDate(BuildContext context) async {
final provider = context.read<TaskProvider>();
final dateSelected = await showDatePicker(
context: context,
initialDate: DateTime.now(),
@ -25,14 +63,28 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
lastDate: DateTime(2025),
);
if (dateSelected != null) {
setState(() {
_dateSelected = DateFormat('dd/MM/yyyy').format(dateSelected);
});
provider.setDate(DateFormat('dd/MM/yyyy').format(dateSelected));
}
}
void _updateTaskProvider() {
final provider = context.read<TaskProvider>();
provider.clear();
provider.getSalesCoordinators();
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateTaskProvider();
});
}
@override
Widget build(BuildContext context) {
final taskProvider = context.watch<TaskProvider>();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
@ -56,13 +108,15 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: SafeArea(
child: Stack(
children: [
SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Container(
padding:
const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
padding: const EdgeInsets.all(20.0)
.copyWith(top: 15, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 30.0)
.copyWith(bottom: 50),
decoration: BoxDecoration(
@ -84,37 +138,69 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
SearchField(
suggestionsDecoration: SuggestionDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Sales Coordinator: Name",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
border: Border.all(
color: Colors.grey.withOpacity(0.5),
),
),
Text(
"ID: 121",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
suggestionItemDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.transparent,
style: BorderStyle.solid,
width: 1.0),
),
searchInputDecoration: InputDecoration(
filled: true,
fillColor: Colors.grey.withOpacity(0.2),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.white,
width: 2.0,
),
borderRadius: BorderRadius.circular(8.0),
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(16.0),
),
borderSide: BorderSide(
color: Colors.white,
width: 2.0,
),
),
],
errorText: !_isSalesCoordinatorValid
? 'Please select a Sales Coordinator'
: null,
),
hint: 'Select Sales Coordinator',
onSuggestionTap: (selectedItem) {
if (selectedItem.item != null) {
taskProvider.setSelectedSalesCoordinator(
selectedItem.item!);
FocusScope.of(context).unfocus();
}
},
onTapOutside: (event) =>
FocusScope.of(context).unfocus(),
marginColor: Colors.grey.shade300,
suggestions: taskProvider.salesCoordinators
.map(
(e) => SearchFieldListItem(
e.name!,
item: e,
child: Text(e.name!),
),
)
.toList(),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
@ -137,12 +223,12 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
SizedBox(
height: 35,
child: CheckboxListTile(
value: _isChecked[0],
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Visit Retailers',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
setState(() {
_isChecked[0] = value!;
});
taskProvider.setSelectedTask(value);
},
title: const Text(
"Visit Retailers",
@ -151,12 +237,12 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
SizedBox(
height: 45,
child: CheckboxListTile(
value: _isChecked[1],
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Update Sales Data',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
setState(() {
_isChecked[1] = value!;
});
taskProvider.setSelectedTask(value);
},
title: const Text(
"Update Sales Data",
@ -165,32 +251,75 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
SizedBox(
height: 45,
child: CheckboxListTile(
value: _isChecked[2],
child: RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Update Liqudation Data',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
setState(() {
_isChecked[2] = value!;
});
taskProvider.setSelectedTask(value);
},
title: const Text(
"Update Liqudation Data",
),
),
),
CheckboxListTile(
value: _isChecked[3],
RadioListTile<String>(
contentPadding: EdgeInsets.zero,
value: 'Collect KYC',
groupValue: taskProvider.selectedTask,
onChanged: (value) {
setState(() {
_isChecked[3] = value!;
});
taskProvider.setSelectedTask(value);
},
title: const Text(
"Collect KYC",
),
),
if (!_isTaskValid &&
(taskProvider.selectedTask?.isEmpty ?? true))
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Please select a task',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
if (taskProvider.selectedTask == 'Collect KYC') ...{
Container(
height: 150,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.0),
),
child: TextFormField(
controller: taskProvider.noteController,
expands: true,
maxLines: null,
minLines: null,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 14.0,
horizontal: 16.0,
),
border: InputBorder.none,
labelText: 'Note',
hintText: 'Enter your note',
errorText: !_isNotesValid
? 'Please enter a note'
: null,
),
),
),
const SizedBox(
height: 20,
)
},
Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
@ -213,41 +342,43 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
Row(
children: [
Checkbox(
value: _isChecked[4],
Radio<String>(
value: "Low",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
setState(() {
_isChecked[4] = value!;
});
taskProvider.setSelectedPriority(value!);
},
),
const Text(
"Low",
),
Checkbox(
value: _isChecked[5],
const Text("Low"),
Radio<String>(
value: "Medium",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
setState(() {
_isChecked[5] = value!;
});
taskProvider.setSelectedPriority(value!);
},
),
const Text(
"Medium",
),
Checkbox(
value: _isChecked[6],
const Text("Medium"),
Radio<String>(
value: "High",
groupValue: taskProvider.selectedPriority,
onChanged: (value) {
setState(() {
_isChecked[6] = value!;
});
taskProvider.setSelectedPriority(value!);
},
),
const Text(
"High",
),
const Text("High"),
],
),
if (!_isPriorityValid &&
(taskProvider.selectedPriority?.isEmpty ??
true))
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Please select a priority',
style: TextStyle(color: Colors.red),
),
),
],
),
),
@ -274,7 +405,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
Row(
children: [
Text(
_dateSelected,
taskProvider.selectedDate,
),
IconButton(
onPressed: () {
@ -293,7 +424,7 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONFIRM',
onPressed: () {},
onPressed: _validateAndSubmit,
),
],
),
@ -301,6 +432,15 @@ class _AssignTasksScreenState extends State<AssignTasksScreen> {
),
),
),
if (taskProvider.isLoading)
Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(),
),
),
],
),
),
);
}

View File

@ -0,0 +1,204 @@
import 'package:cheminova/provider/task_provider.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:provider/provider.dart';
class ConfirmTaskScreen extends StatefulWidget {
const ConfirmTaskScreen({super.key});
@override
State<ConfirmTaskScreen> createState() => _ConfirmTaskScreenState();
}
class _ConfirmTaskScreenState extends State<ConfirmTaskScreen> {
@override
Widget build(BuildContext context) {
final taskProvider = context.watch<TaskProvider>();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: const Center(
child: Text(
'Confirm Task',
style: TextStyle(color: Colors.black87, fontSize: 20),
),
),
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Container(
padding:
const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 30.0)
.copyWith(bottom: 50),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.5),
borderRadius: BorderRadius.circular(26.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Confirm Task',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 20),
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Sales Coordinator:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Text(
taskProvider.selectedSalesCoordinator!.name!,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
Text(
'ID: ${taskProvider.selectedSalesCoordinator!.uniqueId!}',
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
const Text(
'Tasks:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Task: ${taskProvider.selectedTask!}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Priority: ${taskProvider.selectedPriority!}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
'Deadline: ${taskProvider.selectedDate}',
style: const TextStyle(
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
if (taskProvider.selectedTask == 'Collect KYC')
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Note:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
Text(
taskProvider.noteController.text,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 20),
CommonElevatedButton(
isLoading: taskProvider.isLoading,
backgroundColor: const Color(0xff004791),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONFIRM',
onPressed: () => taskProvider.assignTask(context),
),
const SizedBox(height: 20),
CommonElevatedButton(
backgroundColor: const Color(0xff00784C),
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'EDIT',
onPressed: () => Navigator.pop(context),
),
],
),
),
),
),
),
),
);
}
Widget _customContainer({required Widget child}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
// margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: child,
);
}
}

View File

@ -14,10 +14,13 @@ class DataSubmitSuccessfullState extends State<DataSubmitSuccessfull> {
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const HomePage()));
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => const HomePage()),
(Route<dynamic> route) => false, // Remove all routes
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -32,7 +35,11 @@ class DataSubmitSuccessfullState extends State<DataSubmitSuccessfull> {
color: Colors.white,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
const SizedBox(height: 20), // Add some space between the text and the image
Image.asset('assets/check_circle.png', )]))));
const SizedBox(
height: 20), // Add some space between the text and the image
Image.asset(
'assets/check_circle.png',
)
]))));
}
}

View File

@ -1,4 +1,3 @@
import 'package:cheminova/screens/assign_tasks_screen.dart';
import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
@ -79,10 +78,18 @@ class _SelectSalesCoordinatorScreenState
child: ListView.builder(
itemCount: salesCoordinators.length,
itemBuilder: (context, index) {
return _customCard(
return GestureDetector(
onTap: () {
// taskProvider.setSelectedSalesCoordinator(
// salesCoordinators[index],
// );
// Navigator.pop(context);
},
child: _customCard(
name: salesCoordinators[index].name,
id: salesCoordinators[index].id,
tasks: salesCoordinators[index].tasks,
),
);
},
),
@ -98,18 +105,7 @@ class _SelectSalesCoordinatorScreenState
Widget _customCard(
{required String name, required String id, required int tasks}) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const AssignTasksScreen();
},
),
);
},
child: Container(
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.all(8),
@ -147,7 +143,6 @@ class _SelectSalesCoordinatorScreenState
),
],
),
),
);
}
}

View File

@ -0,0 +1,254 @@
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_text_form_field.dart';
import 'package:flutter/material.dart';
class TaskManagementScreen extends StatefulWidget {
const TaskManagementScreen({super.key});
@override
State<TaskManagementScreen> createState() => _TaskManagementScreenState();
}
class _TaskManagementScreenState extends State<TaskManagementScreen> {
final TextEditingController _searchController = TextEditingController();
final List<String> _taskList = [
'Task 1',
'Task 2',
'Task 3',
'Task 4',
'Task 5',
'Task 6',
'Task 7',
'Task 8',
'Task 9',
'Task 10',
];
final List<String> _filters = [
'All',
'Pending',
'Completed',
];
List<String> _filteredTaskList = [];
void _filterTaskList(String query) {
setState(() {
_filteredTaskList = _taskList
.where((task) => task.toLowerCase().contains(query.toLowerCase()))
.toList();
});
}
@override
void initState() {
super.initState();
_filteredTaskList = _taskList;
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CommonAppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20),
),
],
title: const Center(
child: Text(
'Task Management',
style: TextStyle(color: Colors.black87, fontSize: 20),
),
),
),
drawer: const CommonDrawer(),
body: CommonBackground(
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Container(
padding:
const EdgeInsets.all(20.0).copyWith(top: 15, bottom: 30),
margin: const EdgeInsets.symmetric(horizontal: 30.0)
.copyWith(bottom: 50),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.5),
borderRadius: BorderRadius.circular(26.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CommonTextFormField(
controller: _searchController,
onChanged: (value) => _filterTaskList(value),
title: 'Filter by Sales Coordinator:',
),
const SizedBox(height: 20),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_filterChip(
title: 'All',
icon: const Icon(Icons.all_inclusive),
),
const SizedBox(width: 5),
_filterChip(
title: 'Pending',
icon: const Icon(Icons.pending),
),
const SizedBox(width: 5),
_filterChip(
title: 'Completed',
icon: const Icon(Icons.done),
),
],
),
),
const Text(
'Tasks:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
SizedBox(
height: MediaQuery.of(context).size.height * 0.55,
child: ListView.builder(
itemCount: _filteredTaskList.length,
itemBuilder: (context, index) => _taskView(
title: _filteredTaskList[index],
),
),
),
// CommonElevatedButton(
// backgroundColor: const Color(0xff004791),
// borderRadius: 30,
// width: double.infinity,
// height: kToolbarHeight - 10,
// text: 'EDIT',
// onPressed: () => Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const AssignTasksScreen(),
// ),
// ),
// ),
// const SizedBox(height: 20),
// CommonElevatedButton(
// backgroundColor: const Color(0xff00784C),
// borderRadius: 30,
// width: double.infinity,
// height: kToolbarHeight - 10,
// text: 'REASSIGN',
// onPressed: () => Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) =>
// const SelectSalesCoordinatorScreen(),
// ),
// ),
// ),
// const SizedBox(height: 20),
// CommonElevatedButton(
// backgroundColor: const Color(0xff00784C),
// borderRadius: 30,
// width: double.infinity,
// height: kToolbarHeight - 10,
// text: 'MARK AS COMPLETED',
// onPressed: () {},
// ),
],
),
),
),
),
),
),
);
}
Widget _customContainer({required Widget child}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12.0),
// margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
color: const Color(0xffB4D1E5).withOpacity(0.6),
borderRadius: BorderRadius.circular(16.0),
),
child: child,
);
}
Widget _taskView({required String title}) {
return Column(
children: [
_customContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Sales Coordinator: 1',
style: TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
Text(
title,
style: const TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
const Text(
'Status: Pending',
style: TextStyle(
fontFamily: 'Anek',
),
),
const SizedBox(height: 10),
const Text(
'Deadline: 12/12/2024',
style: TextStyle(
fontFamily: 'Anek',
),
),
],
),
),
const SizedBox(height: 10),
],
);
}
Widget _filterChip({required String title, required Widget icon}) {
return Chip(
avatar: icon,
label: Text(
title,
style: const TextStyle(
fontFamily: 'Anek',
),
),
);
}
}

View File

@ -23,10 +23,10 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
void initState() {
super.initState();
// Fetch the PdRd data when the screen is initialized
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = Provider.of<PdRdProvider>(context, listen: false);
provider.getPdRd();
});
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final provider = Provider.of<PdRdProvider>(context, listen: false);
// provider.getPdRd();
// });
}
@override
@ -65,11 +65,14 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
if(selectedDistributor == null || selectedDistributorType == null){
if (selectedDistributor == null ||
selectedDistributorType == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please select distributor type and distributor')),
const SnackBar(
content: Text(
'Please select distributor type and distributor')),
);
}else{
} else {
Navigator.push(
context,
MaterialPageRoute(
@ -85,22 +88,6 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
),
body: Consumer<PdRdProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (provider.pdRdList.isEmpty) {
return const Center(child: Text('No distributors available.'));
}
List<PdRdResponseModel> principalDistributors = provider.pdRdList
.where((item) => item.userType == 'SalesCoOrdinator')
.toList();
List<PdRdResponseModel> retailerDistributors = provider.pdRdList
.where((item) => item.userType != 'SalesCoOrdinator')
.toList();
return Stack(
children: [
Column(
@ -127,6 +114,9 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
onChanged: (value) {
setState(() {
selectedDistributorType = value;
selectedDistributorType == 'Principal Distributor'
? provider.getPd()
: provider.getRd();
selectedDistributor =
null; // Reset distributor selection when type changes
});
@ -148,12 +138,12 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
value: selectedDistributor,
items: (selectedDistributorType ==
'Principal Distributor'
? principalDistributors
: retailerDistributors)
? provider.pdList
: provider.rdList)
.map((PdRdResponseModel distributor) {
return DropdownMenuItem<PdRdResponseModel>(
value: distributor,
child: Text(distributor.name),
child: Text(distributor.name!),
);
}).toList(),
onChanged: (value) {
@ -170,6 +160,13 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
),
],
),
if (provider.isLoading)
Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(),
),
),
],
);
},

View File

@ -12,7 +12,9 @@ class ApiUrls {
static const String notificationUrl = '$baseUrl/get-notification-tm';
static const String fcmUrl = '${baseUrl}kyc/save-fcm-tm';
static const String getProducts = '${baseUrl}product/getAll/user/';
static const String getPdRdUrl =
'inventory/distributors-TM/RetailDistributor';
static const String getPd = 'inventory/distributors-TM/RetailDistributor';
static const String getRd = 'inventory/distributors-TM/PrincipalDistributor';
static const String submitProducts = 'inventory/add-TM';
static const String getSalesCoordinators = 'salescoordinator/getAll-TM';
static const String assignTask = 'task/assign-task';
}

View File

@ -13,6 +13,7 @@ class CommonTextFormField extends StatelessWidget {
final List<TextInputFormatter>? inputFormatters;
final int? maxLength;
final bool obscureText;
final void Function(String)? onChanged;
const CommonTextFormField({
super.key,
@ -26,6 +27,7 @@ class CommonTextFormField extends StatelessWidget {
this.keyboardType,
this.inputFormatters,
this.maxLength,
this.onChanged,
this.obscureText = false,
});
@ -45,6 +47,7 @@ class CommonTextFormField extends StatelessWidget {
readOnly: readOnly ?? false,
maxLines: maxLines,
maxLength: maxLength,
onChanged: onChanged,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
validator: validator,
keyboardType: keyboardType,

View File

@ -1040,6 +1040,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
searchfield:
dependency: "direct main"
description:
name: searchfield
sha256: "913bb61f42d47d82c1adb67047fff3b26dcf6a199f71ecdc9af27c506dda4b48"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
shelf:
dependency: transitive
description:

View File

@ -50,6 +50,7 @@ dependencies:
flutter_local_notifications: ^17.2.1+2
firebase_crashlytics: ^4.0.4
firebase_analytics: ^11.2.1
searchfield: ^1.0.9
dev_dependencies:
flutter_test: