You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
11 KiB
Dart
310 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:test_sa/controllers/validator/validator.dart';
|
|
import 'package:test_sa/extensions/context_extension.dart';
|
|
import 'package:test_sa/extensions/int_extensions.dart';
|
|
import 'package:test_sa/extensions/widget_extensions.dart';
|
|
import 'package:test_sa/modules/applicationDemo/enums/otp_method.dart';
|
|
import 'package:test_sa/modules/applicationDemo/models/application_demo_request_model.dart';
|
|
import 'package:test_sa/modules/applicationDemo/models/role_model.dart';
|
|
import 'package:test_sa/modules/applicationDemo/providers/application_demo_request_provider.dart';
|
|
import 'package:test_sa/modules/applicationDemo/widgets/otp_method_selection_dialog.dart';
|
|
import 'package:test_sa/modules/applicationDemo/widgets/otp_verification_bottom_sheet.dart';
|
|
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
|
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
|
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
|
|
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
|
|
import 'package:test_sa/new_views/common_widgets/custom_app_bar.dart';
|
|
import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart';
|
|
import 'package:test_sa/providers/loading_list_notifier.dart';
|
|
|
|
class DemoRequestFormScreen extends StatefulWidget {
|
|
static const String routeName = '/demo_request_form';
|
|
|
|
const DemoRequestFormScreen({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<DemoRequestFormScreen> createState() => _DemoRequestFormScreenState();
|
|
}
|
|
|
|
class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
final TextEditingController _fullNameController = TextEditingController();
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _mobileController = TextEditingController();
|
|
final TextEditingController _companyNameController = TextEditingController();
|
|
|
|
List<ApplicationDemoRoleModel> _selectedRoles = [];
|
|
bool _isSubmitting = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Fetch roles when the screen initializes
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
final provider = Provider.of<ApplicationDemoRequestProvider>(context, listen: false);
|
|
provider.fetchRoles();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_fullNameController.dispose();
|
|
_emailController.dispose();
|
|
_mobileController.dispose();
|
|
_companyNameController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final provider = Provider.of<ApplicationDemoRequestProvider>(context);
|
|
|
|
return Scaffold(
|
|
backgroundColor: AppColor.background(context),
|
|
appBar: const CustomAppBar(
|
|
title: 'Demo Request',
|
|
),
|
|
body: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
children: [
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
AppTextFormField(
|
|
controller: _fullNameController,
|
|
labelText: 'Full Name',
|
|
hintText: 'Enter your full name',
|
|
textInputType: TextInputType.name,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return 'Full Name is required';
|
|
return null;
|
|
},
|
|
),
|
|
16.height,
|
|
AppTextFormField(
|
|
controller: _emailController,
|
|
labelText: 'Email Address',
|
|
hintText: 'Enter your email address',
|
|
textInputType: TextInputType.emailAddress,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return 'Email is required';
|
|
if (!Validator.isEmail(value)) return 'Invalid email format';
|
|
return null;
|
|
},
|
|
),
|
|
16.height,
|
|
AppTextFormField(
|
|
controller: _mobileController,
|
|
labelText: 'Mobile Number',
|
|
hintText: 'Enter your mobile number',
|
|
textInputType: TextInputType.phone,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return 'Mobile Number is required';
|
|
if (!Validator.isPhoneNumber(value)) return 'Invalid mobile number';
|
|
return null;
|
|
},
|
|
),
|
|
16.height,
|
|
AppTextFormField(
|
|
controller: _companyNameController,
|
|
labelText: 'Company Name',
|
|
hintText: 'Enter your company name',
|
|
textInputType: TextInputType.text,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return 'Company Name is required';
|
|
return null;
|
|
},
|
|
),
|
|
16.height,
|
|
MultipleItemDropDownMenu<ApplicationDemoRoleModel, NullableLoadingProvider>(
|
|
context: context,
|
|
title: 'Select Roles',
|
|
initialValue: _selectedRoles,
|
|
staticData: provider.roles,
|
|
loading: provider.roleLoading,
|
|
showAsFullScreenDialog: true,
|
|
onSelect: (selectedRoles) {
|
|
setState(() {
|
|
_selectedRoles = selectedRoles ?? [];
|
|
});
|
|
},
|
|
),
|
|
],
|
|
).paddingAll(16).expanded,
|
|
FooterActionButton.footerContainer(
|
|
context: context,
|
|
child: AppFilledButton(
|
|
label: 'Submit',
|
|
buttonColor: AppColor.primary10,
|
|
maxWidth: true,
|
|
loading: _isSubmitting,
|
|
onPressed: _isSubmitting ? null : _submitRequest,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _submitRequest() async {
|
|
// Validate form fields
|
|
if (!_formKey.currentState!.validate()) {
|
|
return;
|
|
}
|
|
if (_selectedRoles.isEmpty) {
|
|
context.showConfirmDialog('Please select at least one role');
|
|
return;
|
|
}
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return OtpMethodSelectionDialog(
|
|
onMethodSelected: (OtpMethod method) {
|
|
_submitRequestWithOtpMethod(method);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<void> _submitRequestWithOtpMethod(OtpMethod otpMethod) async {
|
|
setState(() {
|
|
_isSubmitting = true;
|
|
});
|
|
|
|
try {
|
|
final provider = Provider.of<ApplicationDemoRequestProvider>(context, listen: false);
|
|
final otpSent = await provider.sendOtp(
|
|
fullName: _fullNameController.text.trim(),
|
|
phoneNumber: _mobileController.text.trim(),
|
|
email: _emailController.text.trim(),
|
|
typeSend: otpMethod.value,
|
|
);
|
|
|
|
if (otpSent) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
|
|
await context.showBottomSheet(
|
|
OtpVerificationBottomSheet(
|
|
otpMethod: otpMethod.value,
|
|
onOtpVerified: (String otp) async {
|
|
await _verifyOtpAndComplete(otp, otpMethod);
|
|
},
|
|
onResend: () async {
|
|
await provider.sendOtp(
|
|
fullName: _fullNameController.text.trim(),
|
|
phoneNumber: _mobileController.text.trim(),
|
|
email: _emailController.text.trim(),
|
|
typeSend: otpMethod.value,
|
|
);
|
|
},
|
|
onChangeMethod: () {
|
|
_submitRequest();
|
|
},
|
|
),
|
|
isDismissible: false,
|
|
title: 'Verify OTP',
|
|
showCancelButton: true,
|
|
);
|
|
}
|
|
} else {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
context.showConfirmDialog(
|
|
provider.errorMessage ?? 'Failed to send OTP. Please try again.',
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
context.showConfirmDialog('An error occurred: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _verifyOtpAndComplete(String otp, OtpMethod otpMethod) async {
|
|
// Close the bottom sheet
|
|
Navigator.pop(context);
|
|
|
|
// Show loading
|
|
setState(() {
|
|
_isSubmitting = true;
|
|
});
|
|
|
|
try {
|
|
final provider = Provider.of<ApplicationDemoRequestProvider>(context, listen: false);
|
|
|
|
// Verify OTP - pass email or phoneNumber based on selected method
|
|
final otpVerified = await provider.verifyOtp(
|
|
email: otpMethod == OtpMethod.email ? _emailController.text.trim() : null,
|
|
phoneNumber: otpMethod == OtpMethod.sms ? _mobileController.text.trim() : null,
|
|
otpCode: otp,
|
|
);
|
|
|
|
if (otpVerified) {
|
|
// OTP verified, now submit the demo request
|
|
final request = ApplicationDemoRequestModel(
|
|
fullName: _fullNameController.text.trim(),
|
|
emailAddress: _emailController.text.trim(),
|
|
mobileNumber: _mobileController.text.trim(),
|
|
companyId: 0,
|
|
roleIds: _selectedRoles.map((role) => role.identifier ?? '').toList(),
|
|
roleNames: _selectedRoles.map((role) => role.name ?? '').toList(),
|
|
status: 'Pending',
|
|
requestDate: DateTime.now(),
|
|
);
|
|
|
|
final success = await provider.submitApplicationDemoRequest(request);
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
|
|
if (success) {
|
|
context.showConfirmDialog(
|
|
provider.successMessage ?? 'Your demo request has been verified and submitted successfully. We will contact you soon!',
|
|
onTap: () {
|
|
Navigator.of(context).pop(); // Close dialog
|
|
Navigator.of(context).pop(); // Go back to login
|
|
},
|
|
);
|
|
} else {
|
|
context.showConfirmDialog(
|
|
provider.errorMessage ?? 'Failed to submit demo request. Please try again.',
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
// OTP verification failed
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
context.showConfirmDialog(
|
|
provider.errorMessage ?? 'Invalid OTP code. Please try again.',
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
context.showConfirmDialog('OTP verification failed: $e');
|
|
}
|
|
}
|
|
}
|
|
}
|