Update UI of Daily Task screen & kyc

This commit is contained in:
Vaibhav 2024-08-20 17:43:22 +05:30
parent d734bbb6ee
commit f9a96d881a
5 changed files with 443 additions and 323 deletions

View File

@ -14,13 +14,14 @@ class CollectKycProvider extends ChangeNotifier {
CollectKycProvider() { CollectKycProvider() {
getPd(); getPd();
} }
String? selectedTask;
TextEditingController country = TextEditingController(text: "India"); TextEditingController country = TextEditingController(text: "India");
TextEditingController state = TextEditingController(); TextEditingController state = TextEditingController();
TextEditingController city = TextEditingController(); TextEditingController city = TextEditingController();
late TabController tabController; late TabController tabController;
final _apiClient = ApiClient(); final _apiClient = ApiClient();
final selectTaskController= TextEditingController();
final tradeNameController = TextEditingController(); final tradeNameController = TextEditingController();
final nameController = TextEditingController(); final nameController = TextEditingController();
final addressController = TextEditingController(); final addressController = TextEditingController();
@ -119,6 +120,8 @@ class CollectKycProvider extends ChangeNotifier {
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
List<GetPdResponse> pdList = []; List<GetPdResponse> pdList = [];
get taskList => ('Task 1','Task 2');
void setLoading(bool loading) { void setLoading(bool loading) {
_isLoading = loading; _isLoading = loading;
notifyListeners(); notifyListeners();
@ -278,4 +281,9 @@ class CollectKycProvider extends ChangeNotifier {
} }
} }
} }
void setTask(String s) {
selectedTask = s;
notifyListeners();
}
} }

View File

@ -27,8 +27,8 @@ class VisitDealersScreenState extends State<VisitDealersScreen> {
final followUpActionsController = TextEditingController(); final followUpActionsController = TextEditingController();
final nextVisitDateController = TextEditingController(); final nextVisitDateController = TextEditingController();
String selectedPurpose = 'Sales/Liquidation'; String selectedPurpose = 'Sales';
List<String> purposeOptions = ['Sales/Liquidation', 'Dues collection', 'Others']; List<String> purposeOptions = ['Sales', 'Dues collection', 'Others'];
Future<void> _pickImage() async { Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker(); final ImagePicker picker = ImagePicker();

View File

@ -1,114 +1,251 @@
import 'package:cheminova/screens/collect_kyc_screen.dart';
import 'package:cheminova/screens/update_inventory_screen.dart';
import 'package:cheminova/screens/display_sales_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cheminova/screens/Visit_Dealers_screen.dart'; import 'package:cheminova/screens/visit_dealers_screen.dart';
import 'package:cheminova/screens/display_sales_screen.dart';
import 'package:cheminova/screens/update_inventory_screen.dart';
import 'package:cheminova/screens/collect_kyc_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';
import 'package:intl/intl.dart';
class DailyTasksScreen extends StatelessWidget { class DailyTasksScreen extends StatefulWidget {
const DailyTasksScreen({super.key}); const DailyTasksScreen({super.key});
@override
State<DailyTasksScreen> createState() => _DailyTasksScreenState();
}
class _DailyTasksScreenState extends State<DailyTasksScreen> {
final List<String> _tabTitles = ['NEW', 'PENDING', 'COMPLETED'];
int _selectedTabIndex = 0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: CommonAppBar( appBar: _buildAppBar(),
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(
'Daily Tasks',
style: TextStyle(color: Colors.black87, fontSize: 20),
),
),
),
drawer: const CommonDrawer(), drawer: const CommonDrawer(),
body: CommonBackground( body: CommonBackground(
child: SafeArea( child: SafeArea(
child: Padding( child: Column(
padding: const EdgeInsets.all(16.0), crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, _buildCustomTabBar(),
children: [ Expanded(
Expanded( child: _buildTaskList(_selectedTabIndex),
child: ListView( ),
children: [ ],
_buildCustomCard(
'Visit Dealers/Retailers',
'',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const VisitDealersScreen(),
),
);
},
),
const SizedBox(height: 5),
_buildCustomCard(
'Update Sales Data',
'',
onTap: () {
Navigator.push(context,MaterialPageRoute(builder: (context) => const DisplaySalesScreen(),));
},
),
const SizedBox(height: 5),
_buildCustomCard('Update Inventory Data', '',onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const UpdateInventoryScreen(),));
},),
const SizedBox(height: 5),
_buildCustomCard('Collect KYC Documents', '',onTap:() {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const CollectKycScreen(),));
}, ),
],
),
),
],
),
), ),
), ),
), ),
); );
} }
Widget _buildCustomCard(String title, String subtitle, {void Function()? onTap}) { PreferredSizeWidget _buildAppBar() {
return Container( return CommonAppBar(
margin: const EdgeInsets.only(bottom: 10), backgroundColor: Colors.transparent,
decoration: BoxDecoration( elevation: 0,
color: Colors.indigo, actions: [
border: Border.all(color: Colors.white), IconButton(
borderRadius: BorderRadius.circular(10), onPressed: () => Navigator.pop(context),
), icon: Image.asset('assets/Back_attendance.png'),
child: ListTile( padding: const EdgeInsets.only(right: 20),
title: Text(
title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 18,
fontFamily: 'Anek'),
), ),
subtitle: subtitle.isNotEmpty ],
? Text( title: const Text(
subtitle, 'Daily Tasks',
style: const TextStyle(color: Colors.white70, fontSize: 13), style: TextStyle(color: Colors.black87, fontSize: 20),
)
: null,
onTap: onTap,
), ),
); );
} }
Widget _buildCustomTabBar() {
return Container(
height: 60,
margin: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(_tabTitles.length, (index) {
return GestureDetector(
onTap: () => setState(() => _selectedTabIndex = index),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
gradient: _selectedTabIndex == index
? const LinearGradient(
colors: [Color(0xff004791), Color(0xff1a73e8)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
color: _selectedTabIndex == index
? Colors.transparent
: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.black),
boxShadow: [
if (_selectedTabIndex == index)
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Icon(
_getTabIcon(index),
color: _selectedTabIndex == index
? Colors.white
: Colors.black,
),
const SizedBox(width: 8),
Text(
_tabTitles[index],
style: TextStyle(
color: _selectedTabIndex == index
? Colors.white
: Colors.black,
fontWeight: _selectedTabIndex == index
? FontWeight.bold
: FontWeight.normal,
),
),
],
),
),
);
}),
),
);
}
IconData _getTabIcon(int index) {
switch (index) {
case 0:
return Icons.new_releases;
case 1:
return Icons.pending;
case 2:
return Icons.check_circle;
default:
return Icons.new_releases;
}
}
Widget _buildTaskList(int tabIndex) {
List<TaskItem> tasks;
switch (tabIndex) {
case 0:
tasks = _newTasks;
break;
case 1:
tasks = _pendingTasks;
break;
case 2:
tasks = _completedTasks;
break;
default:
tasks = [];
}
return ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: tasks.length,
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) => _buildTaskCard(tasks[index]),
);
}
Widget _buildTaskCard(TaskItem task) {
if (task is KycTaskItem) {
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
children: [
ListTile(
leading: const Icon(Icons.task, color: Colors.blueAccent),
title: Text(
task.title,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w700,
fontSize: 16,
fontFamily: 'Anek',
),
),
trailing:
const Icon(Icons.arrow_forward_ios, color: Colors.black87),
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => task.screen)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Note: ${task.note}'),
Text('Date: ${task.date.toString().split(' ')[0]}'),
Text('Priority: ${task.Low}'),
],
),
),
],
),
);
} else {
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: ListTile(
leading: const Icon(Icons.task, color: Colors.blueAccent),
title: Text(
task.title,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w700,
fontSize: 16,
fontFamily: 'Anek',
),
),
trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black87),
onTap: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => task.screen)),
),
);
}
}
final List<TaskItem> _newTasks = [
KycTaskItem('Collect KYC Documents', const CollectKycScreen(),
'Collect KYC documents from ABC Trader', DateFormat('dd/MM/yyyy').format(DateTime.now()).toString(), 'Priority'),
TaskItem('Update Inventory Data', const UpdateInventoryScreen()),
];
final List<TaskItem> _pendingTasks = [
TaskItem('Update Sales Data', const DisplaySalesScreen()),
TaskItem('Visit Dealers/Retailers', const VisitDealersScreen()),
];
final List<TaskItem> _completedTasks = [
TaskItem('Completed Task 1', const Placeholder()),
TaskItem('Completed Task 2', const Placeholder()),
];
}
class TaskItem {
final String title;
final Widget screen;
TaskItem(this.title, this.screen);
}
class KycTaskItem extends TaskItem {
final String note;
final String date;
final String Low;
KycTaskItem(super.title, super.screen, this.note, this.date, this.Low);
} }

View File

@ -21,7 +21,7 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<CollectKycProvider>( return Consumer<CollectKycProvider>(
builder: (BuildContext context, CollectKycProvider value, builder: (BuildContext context, CollectKycProvider value,
Widget? child) => Widget? child) =>
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView( child: SingleChildScrollView(
@ -43,186 +43,212 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
CommonTextFormField( DropdownButtonFormField<String>(
title: 'Trade Name', decoration: InputDecoration(
fillColor: Colors.white, fillColor: Colors.white,
validator: (String? value) { filled: true,
if (value!.isEmpty) { border: OutlineInputBorder(
return 'Trade Name cannot be empty'; borderRadius: BorderRadius.circular(8),
} borderSide: BorderSide.none)),
return null; hint: Text("Select a task"),
}, value: value.selectedTask,
controller: value.tradeNameController), onChanged: (String? newValue) {
const SizedBox(height: 15), value.setTask(newValue!);
CommonTextFormField( },
title: 'Name', items:["task1", "task2", "task3"].map((String task) {
fillColor: Colors.white, return DropdownMenuItem<String>(
validator: (String? value) { value: task,
if (value!.isEmpty) { child: Text(task),
return 'Name cannot be empty'; );
} }).toList(),
return null; ),
}, IgnorePointer(
controller: value.nameController), ignoring: value.selectedTask == null,
const SizedBox(height: 15), child: Column(
CommonTextFormField( children: [
title: 'Address', const SizedBox(height: 15),
fillColor: Colors.white, CommonTextFormField(
validator: (String? value) { title: 'Trade Name',
if (value!.isEmpty) { fillColor: Colors.white,
return 'Address cannot be empty'; validator: (String? value) {
} if (value!.isEmpty) {
return null; return 'Trade Name cannot be empty';
}, }
controller: value.addressController), return null;
const SizedBox(height: 15), },
CSCPicker( controller: value.tradeNameController),
defaultCountry: CscCountry.India, const SizedBox(height: 15),
CommonTextFormField(
disableCountry: true, title: 'Name',
onCountryChanged: (val) { fillColor: Colors.white,
setState(() { validator: (String? value) {
value.country.text = val; if (value!.isEmpty) {
}); return 'Name cannot be empty';
}, }
onStateChanged: (val) { return null;
setState(() { },
value.state.text = val ?? ''; controller: value.nameController),
}); const SizedBox(height: 15),
}, CommonTextFormField(
onCityChanged: (val) { title: 'Address',
setState(() { fillColor: Colors.white,
value.city.text = val ?? ''; validator: (String? value) {
}); if (value!.isEmpty) {
}, return 'Address cannot be empty';
), }
const SizedBox(height: 15), return null;
CommonTextFormField( },
title: 'District', controller: value.addressController),
fillColor: Colors.white, const SizedBox(height: 15),
validator: (String? value) { CSCPicker(
if (value!.isEmpty) { defaultCountry: CscCountry.India,
return 'District cannot be empty'; disableCountry: true,
} onCountryChanged: (val) {
return null; setState(() {
}, value.country.text = val;
controller: value.districtController), });
const SizedBox(height: 15), },
CommonTextFormField( onStateChanged: (val) {
maxLength: 6, setState(() {
title: 'Pincode', value.state.text = val ?? '';
fillColor: Colors.white, });
inputFormatters: [ },
FilteringTextInputFormatter.digitsOnly onCityChanged: (val) {
setState(() {
value.city.text = val ?? '';
});
},
),
const SizedBox(height: 15),
CommonTextFormField(
title: 'District',
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'District cannot be empty';
}
return null;
},
controller: value.districtController),
const SizedBox(height: 15),
CommonTextFormField(
maxLength: 6,
title: 'Pincode',
fillColor: Colors.white,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
validator: (String? value) {
if (value!.isEmpty) {
return 'Pincode cannot be empty';
}
return null;
},
controller: value.pinCodeController),
const SizedBox(height: 15),
CommonTextFormField(
maxLength: 10,
title: 'Mobile Number',
fillColor: Colors.white,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
validator: (String? value) {
if (value!.isEmpty) {
return 'Mobile Number cannot be empty';
}
return null;
},
controller: value.mobileNumberController),
const SizedBox(height: 15),
CommonTextFormField(
maxLength: 12,
title: 'Aadhar Number',
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'Aadhar Number cannot be empty';
}
return null;
},
controller: value.aadharNumberController),
const SizedBox(height: 15),
CommonTextFormField(
inputFormatters: [
UpperCaseTextFormatter(),
],
maxLength: 10,
title: 'PAN Number',
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'PAN Number cannot be empty';
}
return null;
},
controller: value.panNumberController),
const SizedBox(height: 15),
CommonTextFormField(
inputFormatters: [
UpperCaseTextFormatter(),
],
maxLength: 15,
title: 'GST Number',
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'GST Number cannot be empty';
}
return null;
},
controller: value.gstNumberController),
const SizedBox(height: 15),
DropdownButtonFormField<String>(
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none),
hintText: 'Mapped Principal Distributor'),
value: value.selectedDistributor,
items: value.pdList.map((GetPdResponse pd) {
return DropdownMenuItem<String>(
value: pd.sId, child: Text(pd.name ?? ''));
}).toList(),
onChanged: (String? newValue) {
setState(() {
value.selectedDistributor = newValue;
});
},
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Mapped Principal Distributor cannot be empty';
}
return null;
},
),
const SizedBox(height: 30),
Align(
alignment: Alignment.center,
child: CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONTINUE',
backgroundColor: const Color(0xff004791),
onPressed: () {
if (value.retailerDetailsFormKey.currentState!
.validate()) {
value.tabController.animateTo(1);
}
},
),
),
], ],
validator: (String? value) {
if (value!.isEmpty) {
return 'Pincode cannot be empty';
}
return null;
},
controller: value.pinCodeController),
const SizedBox(height: 15),
CommonTextFormField(
maxLength: 10,
title: 'Mobile Number',
fillColor: Colors.white,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
validator: (String? value) {
if (value!.isEmpty) {
return 'Mobile Number cannot be empty';
}
return null;
},
controller: value.mobileNumberController),
const SizedBox(height: 15),
CommonTextFormField(
maxLength: 12,
title: 'Aadhar Number',
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'Aadhar Number cannot be empty';
}
return null;
},
controller: value.aadharNumberController),
const SizedBox(height: 15),
CommonTextFormField(
inputFormatters: [
UpperCaseTextFormatter(),
],
maxLength: 10,
title: 'PAN Number',
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'PAN Number cannot be empty';
}
return null;
},
controller: value.panNumberController),
const SizedBox(height: 15),
CommonTextFormField(
inputFormatters: [
UpperCaseTextFormatter(),
],
maxLength: 15,
title: 'GST Number',
fillColor: Colors.white,
validator: (String? value) {
if (value!.isEmpty) {
return 'GST Number cannot be empty';
}
return null;
},
controller: value.gstNumberController),
const SizedBox(height: 15),
DropdownButtonFormField<String>(
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none),
hintText: 'Mapped Principal Distributor'),
value: value.selectedDistributor,
items: value.pdList.map((GetPdResponse pd) {
return DropdownMenuItem<String>(
value: pd.sId, child: Text(pd.name ?? ''));
}).toList(),
onChanged: (String? newValue) {
setState(() {
value.selectedDistributor = newValue;
});
},
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Mapped Principal Distributor cannot be empty';
}
return null;
},
),
const SizedBox(height: 30),
Align(
alignment: Alignment.center,
child: CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'CONTINUE',
backgroundColor: const Color(0xff004791),
onPressed: () {
if (value.retailerDetailsFormKey.currentState!
.validate()) {
value.tabController.animateTo(1);
}
},
), ),
), ),
], ],
@ -235,53 +261,3 @@ class RetailerDetailsScreenState extends State<RetailerDetailsScreen> {
)); ));
} }
} }
class CustomCountryStateCityPicker extends StatelessWidget {
final TextEditingController country;
final TextEditingController state;
final TextEditingController city;
final Color dialogColor;
final InputDecoration textFieldDecoration;
const CustomCountryStateCityPicker({
Key? key,
required this.country,
required this.state,
required this.city,
required this.dialogColor,
required this.textFieldDecoration,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
TextFormField(
controller: country,
decoration: textFieldDecoration.copyWith(
labelText: 'Country',
enabled: false,
),
),
const SizedBox(height: 15),
TextFormField(
controller: state,
decoration: textFieldDecoration.copyWith(labelText: 'State'),
readOnly: true,
onTap: () {
// Implement state selection logic for India
},
),
const SizedBox(height: 15),
TextFormField(
controller: city,
decoration: textFieldDecoration.copyWith(labelText: 'City'),
readOnly: true,
onTap: () {
// Implement city selection logic based on selected state
},
),
],
);
}
}

View File

@ -82,7 +82,6 @@ class _UpdateInventoryScreenState extends State<UpdateInventoryScreen> {
}).toList(), }).toList(),
onChanged: (val) => onChanged: (val) =>
value.updatePdRdValue(val), value.updatePdRdValue(val),
// Disable the dropdown if no distributor type is selected
isExpanded: true, isExpanded: true,
isDense: true, isDense: true,
iconSize: 24, iconSize: 24,