636 lines
23 KiB
Dart
636 lines
23 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:siro_service/constant/colors.dart';
|
|
import 'package:siro_service/constant/links.dart';
|
|
import 'package:siro_service/controller/functions/crud.dart';
|
|
import 'package:siro_service/controller/mainController/main_controller.dart';
|
|
import 'package:siro_service/controller/mainController/pages/best_driver_page.dart';
|
|
import 'package:siro_service/controller/mainController/pages/complaint.dart';
|
|
import 'package:siro_service/controller/mainController/pages/edit_car_plate.dart';
|
|
import 'package:siro_service/controller/mainController/pages/passengers_cant_regster.dart';
|
|
import 'package:siro_service/views/widgets/elevated_btn.dart';
|
|
import 'package:siro_service/views/widgets/my_dialog.dart';
|
|
import 'package:siro_service/views/widgets/my_textField.dart';
|
|
|
|
import '../../constant/style.dart';
|
|
import '../../controller/mainController/pages/add_car.dart';
|
|
import '../../controller/mainController/pages/drivers_cant_register.dart';
|
|
import '../../controller/mainController/pages/new_driver.dart';
|
|
import '../../controller/mainController/pages/welcome_call.dart';
|
|
import '../../main.dart';
|
|
import '../widgets/my_scafold.dart';
|
|
|
|
class Main extends StatelessWidget {
|
|
Main({super.key});
|
|
|
|
final MainController mainController = Get.put(MainController());
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MyScaffold(title: 'Siro Service'.tr, isleading: false, body: [
|
|
Container(
|
|
color: AppColor.surfaceColor,
|
|
child: SingleChildScrollView(
|
|
physics: const BouncingScrollPhysics(),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// --- Header Section ---
|
|
_buildHeader(),
|
|
|
|
// --- Statistics Section ---
|
|
_buildStatsSection(),
|
|
|
|
// --- Actions Section ---
|
|
_buildCategoryTitle('🔍 Search & Inquiries'.tr),
|
|
_buildGridSection([
|
|
ServiceItem(
|
|
title: 'passenger details by phone'.tr,
|
|
icon: Icons.person_search_rounded,
|
|
color: Colors.blue,
|
|
onTap: () => _showSearchDialog(
|
|
title: 'insert passenger phone'.tr,
|
|
controller: mainController.passengerPhoneController,
|
|
onSearch: () => mainController.searchPassengerByPhone(),
|
|
),
|
|
),
|
|
ServiceItem(
|
|
title: 'Driver details by phone'.tr,
|
|
icon: Icons.contact_phone_rounded,
|
|
color: Colors.indigo,
|
|
onTap: () => _showSearchDialog(
|
|
title: 'insert Driver phone'.tr,
|
|
controller: mainController.driverPhoneController,
|
|
onSearch: () => mainController.searchDriverByPhone(),
|
|
),
|
|
),
|
|
ServiceItem(
|
|
title: 'Driver details by national number'.tr,
|
|
icon: Icons.badge_rounded,
|
|
color: Colors.teal,
|
|
onTap: () => _showSearchDialog(
|
|
title: 'insert Driver national'.tr,
|
|
controller: mainController.driverPhoneController,
|
|
type: TextInputType.number,
|
|
onSearch: () => mainController.searchDriverByNational(),
|
|
),
|
|
),
|
|
]),
|
|
|
|
_buildCategoryTitle('⏳ Approval Queue'.tr),
|
|
_buildGridSection([
|
|
ServiceItem(
|
|
title: 'Drivers waitting Register'.tr,
|
|
icon: Icons.pending_actions_rounded,
|
|
color: Colors.orange,
|
|
onTap: () async {
|
|
await mainController.getDriverWantCompleteRegistration();
|
|
Get.to(() => DriversCantRegister());
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: 'Drivers phones not register'.tr,
|
|
icon: Icons.phone_disabled_rounded,
|
|
color: Colors.blueGrey,
|
|
onTap: () async {
|
|
await mainController.getDriversPhoneNotComplete();
|
|
Get.to(() => DriversCantRegister());
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: 'Drivers Activity'.tr,
|
|
icon: Icons.assessment_rounded,
|
|
color: Colors.blueAccent,
|
|
onTap: () async {
|
|
await mainController.getDriversPhoneNotComplete();
|
|
Get.to(() => DriversCantRegister());
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: 'Drivers Cant Register'.tr,
|
|
icon: Icons.car_crash_rounded,
|
|
color: Colors.redAccent,
|
|
onTap: () async {
|
|
await mainController.getDriverNotCompleteRegistration();
|
|
Get.to(() => DriversCantRegister());
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: 'Passengers Cant Register'.tr,
|
|
icon: Icons.group_off_rounded,
|
|
color: Colors.deepOrange,
|
|
onTap: () async {
|
|
await mainController.getPassengerNotCompleteRegistration();
|
|
Get.to(() => PassengersCantRegister());
|
|
},
|
|
),
|
|
]),
|
|
|
|
_buildCategoryTitle('⚡ Quick Actions'.tr),
|
|
_buildGridSection([
|
|
ServiceItem(
|
|
title: 'Register new driver'.tr,
|
|
icon: Icons.person_add_rounded,
|
|
color: Colors.green,
|
|
onTap: () => Get.to(() => RegisterCaptain()),
|
|
),
|
|
ServiceItem(
|
|
title: 'Add Driver Who Wants to Work'.tr,
|
|
icon: Icons.handshake_rounded,
|
|
color: Colors.lightGreen,
|
|
onTap: () => _showAddDriverDialog(),
|
|
),
|
|
ServiceItem(
|
|
title: 'Add Car Who Wants to Work'.tr,
|
|
icon: Icons.add_road_rounded,
|
|
color: Colors.cyan,
|
|
onTap: () => _showAddCarDialog(),
|
|
),
|
|
]),
|
|
|
|
_buildCategoryTitle('🚗 Vehicle Management'.tr),
|
|
_buildGridSection([
|
|
ServiceItem(
|
|
title: 'Add car'.tr,
|
|
icon: Icons.add_circle_outline_rounded,
|
|
color: Colors.purple,
|
|
onTap: () async {
|
|
await mainController.getdriverWithoutCar();
|
|
if (mainController.driverWithoutCar.isNotEmpty) {
|
|
Get.to(() => const AddCar());
|
|
}
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: 'Edit car plate'.tr,
|
|
icon: Icons.edit_attributes_rounded,
|
|
color: Colors.amber,
|
|
onTap: () async {
|
|
await mainController.getCarPlateNotEdit();
|
|
if (mainController.carPlateNotEdit.isNotEmpty) {
|
|
Get.to(() => const EditCarPlate());
|
|
}
|
|
},
|
|
),
|
|
]),
|
|
|
|
_buildCategoryTitle('📊 Reporting & Quality'.tr),
|
|
_buildGridSection([
|
|
ServiceItem(
|
|
title: "View complaint".tr,
|
|
icon: Icons.report_problem_rounded,
|
|
color: Colors.deepOrange,
|
|
onTap: () => Get.to(() => const Complaint()),
|
|
),
|
|
ServiceItem(
|
|
title: "Welcome call".tr,
|
|
icon: Icons.ring_volume_rounded,
|
|
color: Colors.pink,
|
|
onTap: () async {
|
|
await mainController.getNewDriverRegister();
|
|
Get.to(() => const WelcomeCall());
|
|
},
|
|
),
|
|
ServiceItem(
|
|
title: "best driver".tr,
|
|
icon: Icons.emoji_events_rounded,
|
|
color: Colors.orangeAccent,
|
|
onTap: () async {
|
|
await mainController.getNewDriverRegister();
|
|
Get.to(() => DriverTheBest());
|
|
},
|
|
),
|
|
]),
|
|
|
|
const SizedBox(height: 32),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
]);
|
|
}
|
|
|
|
Widget _buildHeader() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: const BoxDecoration(
|
|
color: AppColor.primaryColor,
|
|
borderRadius: BorderRadius.only(
|
|
bottomLeft: Radius.circular(32),
|
|
bottomRight: Radius.circular(32),
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 10),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Welcome back,'.tr,
|
|
style: AppStyle.title.copyWith(color: Colors.white70),
|
|
),
|
|
Text(
|
|
'Service Agent'.tr,
|
|
style: AppStyle.headTitle2.copyWith(color: Colors.white),
|
|
),
|
|
],
|
|
),
|
|
Row(
|
|
children: [
|
|
IconButton(
|
|
icon:
|
|
const Icon(Icons.refresh_rounded, color: Colors.white),
|
|
onPressed: () => mainController.refreshDashboardStats(),
|
|
),
|
|
const SizedBox(width: 8),
|
|
CircleAvatar(
|
|
radius: 24,
|
|
backgroundColor: Colors.white24,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.logout, color: Colors.white),
|
|
onPressed: () {
|
|
box.erase();
|
|
Get.offAllNamed('/login');
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
// --- Custom Search Bar ---
|
|
Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
decoration: AppStyle.boxDecoration.copyWith(
|
|
color: Colors.white.withValues(alpha: 0.95),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: TextField(
|
|
decoration: InputDecoration(
|
|
hintText: 'Quick Search...'.tr,
|
|
border: InputBorder.none,
|
|
icon: const Icon(Icons.search, color: AppColor.primaryColor),
|
|
),
|
|
onSubmitted: (val) {
|
|
// Global search logic
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatsSection() {
|
|
return GetBuilder<MainController>(
|
|
builder: (controller) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
|
|
child: Row(
|
|
children: [
|
|
_buildStatCard(
|
|
'Pending'.tr,
|
|
controller.isLoading
|
|
? '...'
|
|
: controller.driverWantCompleteRegistration.length
|
|
.toString(),
|
|
Icons.timer_rounded,
|
|
Colors.orange,
|
|
),
|
|
const SizedBox(width: 12),
|
|
_buildStatCard(
|
|
'New'.tr,
|
|
controller.isLoading
|
|
? '...'
|
|
: controller.newDriverRegister.length.toString(),
|
|
Icons.person_add_alt_1_rounded,
|
|
Colors.green,
|
|
),
|
|
const SizedBox(width: 12),
|
|
_buildStatCard(
|
|
'Issues'.tr,
|
|
controller.isLoading
|
|
? '...'
|
|
: controller.driverNotCompleteRegistration.length
|
|
.toString(),
|
|
Icons.warning_rounded,
|
|
Colors.red,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildStatCard(
|
|
String title, String count, IconData icon, Color color) {
|
|
return Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: AppStyle.boxDecoration,
|
|
child: Column(
|
|
children: [
|
|
Icon(icon, color: color, size: 24),
|
|
const SizedBox(height: 8),
|
|
Text(count, style: AppStyle.headTitle2.copyWith(fontSize: 20)),
|
|
Text(title,
|
|
style: AppStyle.subtitle.copyWith(color: AppColor.accentColor)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildCategoryTitle(String title) {
|
|
return Padding(
|
|
padding: const EdgeInsets.fromLTRB(20, 24, 20, 12),
|
|
child: Text(
|
|
title,
|
|
style: AppStyle.headTitle2.copyWith(fontSize: 18),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildGridSection(List<ServiceItem> items) {
|
|
return GridView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 2,
|
|
crossAxisSpacing: 12,
|
|
mainAxisSpacing: 12,
|
|
childAspectRatio: 1.15,
|
|
),
|
|
itemCount: items.length,
|
|
itemBuilder: (context, index) {
|
|
final item = items[index];
|
|
return _buildServiceCard(item);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildServiceCard(ServiceItem item) {
|
|
return InkWell(
|
|
onTap: item.onTap,
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: Container(
|
|
decoration: AppStyle.boxDecoration1,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: item.color.withValues(alpha: 0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(item.icon, color: item.color, size: 30),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: Text(
|
|
item.title,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.title
|
|
.copyWith(fontSize: 13, fontWeight: FontWeight.w600),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showSearchDialog({
|
|
required String title,
|
|
required TextEditingController controller,
|
|
required VoidCallback onSearch,
|
|
TextInputType type = TextInputType.phone,
|
|
}) {
|
|
MyDialog().getDialog(
|
|
title,
|
|
'Search Details'.tr,
|
|
Form(
|
|
key: mainController.formKey,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
MyTextForm(
|
|
controller: controller,
|
|
label: title,
|
|
hint: title,
|
|
type: type,
|
|
validator: (val) {
|
|
if (val == null || val.isEmpty) {
|
|
return 'Please enter a value'.tr;
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
onSearch,
|
|
);
|
|
}
|
|
|
|
void _showAddDriverDialog() {
|
|
Get.defaultDialog(
|
|
barrierDismissible: false,
|
|
title: "Add Driver Who Wants to Work".tr,
|
|
titleStyle: AppStyle.headTitle2,
|
|
content: SizedBox(
|
|
width: Get.width * .8,
|
|
height: 350,
|
|
child: Form(
|
|
key: mainController.formKey,
|
|
child: ListView(
|
|
padding: const EdgeInsets.all(8),
|
|
children: [
|
|
MyTextForm(
|
|
controller: mainController.driverNameController,
|
|
label: 'Insert Name of Driver'.tr,
|
|
hint: 'Insert Name of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.nationalIdController,
|
|
label: 'Insert national ID of Driver'.tr,
|
|
hint: 'Insert national ID of Driver'.tr,
|
|
type: TextInputType.number),
|
|
MyTextForm(
|
|
controller: mainController.phoneController,
|
|
label: 'Insert phone of Driver'.tr,
|
|
hint: 'Insert phone of Driver'.tr,
|
|
type: TextInputType.phone),
|
|
MyTextForm(
|
|
controller: mainController.licenseTypeController,
|
|
label: 'Insert license type of Driver'.tr,
|
|
hint: 'Insert license type of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.siteDriverController,
|
|
label: 'Insert site of Driver'.tr,
|
|
hint: 'Insert site of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.birthDateController,
|
|
label: 'Insert birth_date of Driver'.tr,
|
|
hint: 'YYYY-MM-DD'.tr,
|
|
type: TextInputType.datetime),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
confirm: MyElevatedButton(
|
|
title: 'Add'.tr,
|
|
onPressed: () async {
|
|
var res =
|
|
await CRUD().post(link: AppLink.addDriverWantWork, payload: {
|
|
"driver_name": mainController.driverNameController.text,
|
|
"national_id": mainController.nationalIdController.text,
|
|
"birth_date": mainController.birthDateController.text,
|
|
"site": mainController.siteDriverController.text,
|
|
"license_type": mainController.licenseTypeController.text,
|
|
"phone": mainController.phoneController.text,
|
|
});
|
|
if (res != 'failure' && res['status'] == 'success') {
|
|
Get.back();
|
|
mainController.driverNameController.clear();
|
|
mainController.nationalIdController.clear();
|
|
mainController.birthDateController.clear();
|
|
mainController.licenseTypeController.clear();
|
|
mainController.siteDriverController.clear();
|
|
mainController.phoneController.clear();
|
|
Get.snackbar('Success'.tr, 'Added successfully'.tr,
|
|
backgroundColor: AppColor.greenColor, colorText: Colors.white);
|
|
}
|
|
},
|
|
kolor: AppColor.greenColor,
|
|
),
|
|
cancel: MyElevatedButton(
|
|
title: 'Cancel'.tr,
|
|
kolor: AppColor.redColor,
|
|
onPressed: () => Get.back()),
|
|
);
|
|
}
|
|
|
|
void _showAddCarDialog() {
|
|
Get.defaultDialog(
|
|
barrierDismissible: false,
|
|
title: "Add Car Who Wants to Work".tr,
|
|
titleStyle: AppStyle.headTitle2,
|
|
content: SizedBox(
|
|
width: Get.width * .8,
|
|
height: 350,
|
|
child: Form(
|
|
key: mainController.formKey,
|
|
child: ListView(
|
|
padding: const EdgeInsets.all(8),
|
|
children: [
|
|
MyTextForm(
|
|
controller: mainController.carOwnerWorkController,
|
|
label: 'Insert Name of Owner'.tr,
|
|
hint: 'Insert Name of Owner'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.carNumberController,
|
|
label: 'Insert car_number of Driver'.tr,
|
|
hint: 'Insert car_number of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.phoneCarController,
|
|
label: 'Insert phone of Owner'.tr,
|
|
hint: 'Insert phone of Owner'.tr,
|
|
type: TextInputType.phone),
|
|
MyTextForm(
|
|
controller: mainController.manufactureYearController,
|
|
label: 'Insert year of Car'.tr,
|
|
hint: 'Insert year of Car'.tr,
|
|
type: TextInputType.number),
|
|
MyTextForm(
|
|
controller: mainController.carModelController,
|
|
label: 'Insert car_model of Driver'.tr,
|
|
hint: 'Insert car_model of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.siteCarController,
|
|
label: 'Insert site of Owner'.tr,
|
|
hint: 'Insert site of Owner'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.carTypeController,
|
|
label: 'Insert car_type of Driver'.tr,
|
|
hint: 'Insert car_type of Driver'.tr,
|
|
type: TextInputType.name),
|
|
MyTextForm(
|
|
controller: mainController.registrationDateController,
|
|
label: 'Insert registration_date of Car'.tr,
|
|
hint: 'YYYY-MM-DD'.tr,
|
|
type: TextInputType.datetime),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
confirm: MyElevatedButton(
|
|
title: 'Add'.tr,
|
|
onPressed: () async {
|
|
var res = await CRUD().post(link: AppLink.addCarWantWork, payload: {
|
|
"owner_name": mainController.carOwnerWorkController.text,
|
|
"car_number": mainController.carNumberController.text,
|
|
"manufacture_year": mainController.manufactureYearController.text,
|
|
"car_model": mainController.carModelController.text,
|
|
"car_type": mainController.carTypeController.text,
|
|
"site": mainController.siteCarController.text,
|
|
"registration_date": mainController.registrationDateController.text,
|
|
"phone": mainController.phoneCarController.text,
|
|
});
|
|
if (res != 'failure' && res['status'] == 'success') {
|
|
Get.back();
|
|
mainController.carOwnerWorkController.clear();
|
|
mainController.carNumberController.clear();
|
|
mainController.manufactureYearController.clear();
|
|
mainController.carModelController.clear();
|
|
mainController.siteCarController.clear();
|
|
mainController.carTypeController.clear();
|
|
mainController.registrationDateController.clear();
|
|
mainController.phoneCarController.clear();
|
|
Get.snackbar('Success'.tr, 'Added successfully'.tr,
|
|
backgroundColor: AppColor.greenColor, colorText: Colors.white);
|
|
}
|
|
},
|
|
kolor: AppColor.greenColor,
|
|
),
|
|
cancel: MyElevatedButton(
|
|
title: 'Cancel'.tr,
|
|
kolor: AppColor.redColor,
|
|
onPressed: () => Get.back()),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ServiceItem {
|
|
final String title;
|
|
final IconData icon;
|
|
final Color color;
|
|
final VoidCallback onTap;
|
|
|
|
ServiceItem({
|
|
required this.title,
|
|
required this.icon,
|
|
required this.color,
|
|
required this.onTap,
|
|
});
|
|
}
|