|
|
|
@ -20,6 +20,12 @@ class EnvironmentConfigSheet extends StatefulWidget {
|
|
|
|
class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
AppEnvironmentPreset? _selectedPreset;
|
|
|
|
AppEnvironmentPreset? _selectedPreset;
|
|
|
|
bool _isLoading = true;
|
|
|
|
bool _isLoading = true;
|
|
|
|
|
|
|
|
bool _isCustomMode = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Text controllers for custom URLs
|
|
|
|
|
|
|
|
final TextEditingController _baseUrlController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _rcBaseUrlController = TextEditingController();
|
|
|
|
|
|
|
|
final TextEditingController _paymentUrlController = TextEditingController();
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
void initState() {
|
|
|
|
@ -27,13 +33,37 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
_loadCurrentEnvironment();
|
|
|
|
_loadCurrentEnvironment();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
|
|
_baseUrlController.dispose();
|
|
|
|
|
|
|
|
_rcBaseUrlController.dispose();
|
|
|
|
|
|
|
|
_paymentUrlController.dispose();
|
|
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _loadCurrentEnvironment() async {
|
|
|
|
Future<void> _loadCurrentEnvironment() async {
|
|
|
|
|
|
|
|
final isCustom = EnvironmentConfigService.instance.isCustomEnvironment();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isCustom) {
|
|
|
|
|
|
|
|
final customUrls = EnvironmentConfigService.instance.getCustomUrls();
|
|
|
|
|
|
|
|
_baseUrlController.text = customUrls['baseUrl'] ?? '';
|
|
|
|
|
|
|
|
_rcBaseUrlController.text = customUrls['rcBaseUrl'] ?? '';
|
|
|
|
|
|
|
|
_paymentUrlController.text = customUrls['paymentUrl'] ?? '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
|
|
_isCustomMode = true;
|
|
|
|
|
|
|
|
_selectedPreset = null;
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
final currentPreset = EnvironmentConfigService.instance.getCurrentPreset();
|
|
|
|
final currentPreset = EnvironmentConfigService.instance.getCurrentPreset();
|
|
|
|
setState(() {
|
|
|
|
setState(() {
|
|
|
|
_selectedPreset = currentPreset;
|
|
|
|
_selectedPreset = currentPreset;
|
|
|
|
|
|
|
|
_isCustomMode = false;
|
|
|
|
_isLoading = false;
|
|
|
|
_isLoading = false;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
@ -116,7 +146,9 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
SizedBox(height: 4.h),
|
|
|
|
SizedBox(height: 4.h),
|
|
|
|
Text(
|
|
|
|
Text(
|
|
|
|
_selectedPreset?.label ?? LocaleKeys.production.tr(context: context),
|
|
|
|
_isCustomMode
|
|
|
|
|
|
|
|
? 'Custom'
|
|
|
|
|
|
|
|
: (_selectedPreset?.label ?? LocaleKeys.production.tr(context: context)),
|
|
|
|
style: TextStyle(fontSize: 12.f, color: AppColors.blackBgColor),
|
|
|
|
style: TextStyle(fontSize: 12.f, color: AppColors.blackBgColor),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
@ -135,12 +167,15 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
Wrap(
|
|
|
|
Wrap(
|
|
|
|
spacing: 8.w,
|
|
|
|
spacing: 8.w,
|
|
|
|
runSpacing: 8.h,
|
|
|
|
runSpacing: 8.h,
|
|
|
|
children: kEnvironmentPresets.map((preset) {
|
|
|
|
children: [
|
|
|
|
final isSelected = _selectedPreset?.label == preset.label;
|
|
|
|
// Preset environment chips
|
|
|
|
|
|
|
|
...kEnvironmentPresets.map((preset) {
|
|
|
|
|
|
|
|
final isSelected = !_isCustomMode && _selectedPreset?.label == preset.label;
|
|
|
|
return InkWell(
|
|
|
|
return InkWell(
|
|
|
|
onTap: () {
|
|
|
|
onTap: () {
|
|
|
|
setState(() {
|
|
|
|
setState(() {
|
|
|
|
_selectedPreset = preset;
|
|
|
|
_selectedPreset = preset;
|
|
|
|
|
|
|
|
_isCustomMode = false;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
borderRadius: BorderRadius.circular(20.r),
|
|
|
|
borderRadius: BorderRadius.circular(20.r),
|
|
|
|
@ -176,12 +211,141 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}).toList(),
|
|
|
|
}).toList(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Custom chip
|
|
|
|
|
|
|
|
InkWell(
|
|
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
|
|
_isCustomMode = true;
|
|
|
|
|
|
|
|
_selectedPreset = null;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(20.r),
|
|
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
|
|
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
|
|
color: _isCustomMode ? AppColors.infoColor : AppColors.whiteColor,
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(20.r),
|
|
|
|
|
|
|
|
border: Border.all(
|
|
|
|
|
|
|
|
color: _isCustomMode ? AppColors.infoColor : AppColors.greyInfoTextColor.withValues(alpha: 0.3),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
|
|
|
children: [
|
|
|
|
|
|
|
|
if (_isCustomMode)
|
|
|
|
|
|
|
|
Icon(
|
|
|
|
|
|
|
|
Icons.check_circle,
|
|
|
|
|
|
|
|
size: 18.w,
|
|
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
if (_isCustomMode) SizedBox(width: 6.w),
|
|
|
|
|
|
|
|
Icon(
|
|
|
|
|
|
|
|
Icons.edit_outlined,
|
|
|
|
|
|
|
|
size: 16.w,
|
|
|
|
|
|
|
|
color: _isCustomMode ? Colors.white : AppColors.blackBgColor,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
SizedBox(width: 4.w),
|
|
|
|
|
|
|
|
Text(
|
|
|
|
|
|
|
|
'Custom',
|
|
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
|
|
fontSize: 14.f,
|
|
|
|
|
|
|
|
color: _isCustomMode ? Colors.white : AppColors.blackBgColor,
|
|
|
|
|
|
|
|
fontWeight: _isCustomMode ? FontWeight.bold : FontWeight.normal,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SizedBox(height: 24.h),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Custom URL Input Fields
|
|
|
|
|
|
|
|
if (_isCustomMode) ...[
|
|
|
|
|
|
|
|
Text('Custom URLs', style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.bold)),
|
|
|
|
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Base URL
|
|
|
|
|
|
|
|
TextField(
|
|
|
|
|
|
|
|
controller: _baseUrlController,
|
|
|
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
|
|
|
labelText: 'Base URL',
|
|
|
|
|
|
|
|
hintText: 'https://example.com/',
|
|
|
|
|
|
|
|
prefixIcon: Icon(Icons.link, size: 20.w),
|
|
|
|
|
|
|
|
border: OutlineInputBorder(
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
filled: true,
|
|
|
|
|
|
|
|
fillColor: AppColors.whiteColor,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
style: TextStyle(fontSize: 14.f),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// RC Base URL
|
|
|
|
|
|
|
|
TextField(
|
|
|
|
|
|
|
|
controller: _rcBaseUrlController,
|
|
|
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
|
|
|
labelText: 'RC Base URL',
|
|
|
|
|
|
|
|
hintText: 'https://rc.example.com/',
|
|
|
|
|
|
|
|
prefixIcon: Icon(Icons.link, size: 20.w),
|
|
|
|
|
|
|
|
border: OutlineInputBorder(
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
filled: true,
|
|
|
|
|
|
|
|
fillColor: AppColors.whiteColor,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
style: TextStyle(fontSize: 14.f),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Payment URL
|
|
|
|
|
|
|
|
TextField(
|
|
|
|
|
|
|
|
controller: _paymentUrlController,
|
|
|
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
|
|
|
labelText: 'Payment URL',
|
|
|
|
|
|
|
|
hintText: 'https://example.com/payment/SendPayFortRequest.aspx',
|
|
|
|
|
|
|
|
prefixIcon: Icon(Icons.payment, size: 20.w),
|
|
|
|
|
|
|
|
border: OutlineInputBorder(
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
filled: true,
|
|
|
|
|
|
|
|
fillColor: AppColors.whiteColor,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
style: TextStyle(fontSize: 14.f),
|
|
|
|
|
|
|
|
),
|
|
|
|
SizedBox(height: 24.h),
|
|
|
|
SizedBox(height: 24.h),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// Environment Details
|
|
|
|
// Environment Details
|
|
|
|
if (_selectedPreset != null) ...[
|
|
|
|
if (_isCustomMode && _baseUrlController.text.isNotEmpty) ...[
|
|
|
|
|
|
|
|
Text(LocaleKeys.environmentDetails.tr(context: context), style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.bold)),
|
|
|
|
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
|
|
|
|
Container(
|
|
|
|
|
|
|
|
padding: EdgeInsets.all(16.w),
|
|
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
|
|
color: AppColors.whiteColor,
|
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
|
|
|
border: Border.all(
|
|
|
|
|
|
|
|
color: AppColors.greyInfoTextColor.withValues(alpha: 0.2),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
|
|
children: [
|
|
|
|
|
|
|
|
_buildDetailRow(LocaleKeys.baseUrl.tr(context: context), _baseUrlController.text),
|
|
|
|
|
|
|
|
Divider(height: 20.h),
|
|
|
|
|
|
|
|
_buildDetailRow(LocaleKeys.rcUrl.tr(context: context), _rcBaseUrlController.text),
|
|
|
|
|
|
|
|
Divider(height: 20.h),
|
|
|
|
|
|
|
|
_buildDetailRow('Payment URL', _paymentUrlController.text),
|
|
|
|
|
|
|
|
Divider(height: 20.h),
|
|
|
|
|
|
|
|
_buildDetailRow(LocaleKeys.payfortMode.tr(context: context), LocaleKeys.sandbox.tr(context: context)),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
] else if (_selectedPreset != null) ...[
|
|
|
|
Text(LocaleKeys.environmentDetails.tr(context: context), style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.bold)),
|
|
|
|
Text(LocaleKeys.environmentDetails.tr(context: context), style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.bold)),
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
SizedBox(height: 12.h),
|
|
|
|
Container(
|
|
|
|
Container(
|
|
|
|
@ -199,7 +363,7 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
Divider(height: 20.h),
|
|
|
|
Divider(height: 20.h),
|
|
|
|
_buildDetailRow(LocaleKeys.rcUrl.tr(context: context), _selectedPreset!.rcBaseUrl),
|
|
|
|
_buildDetailRow(LocaleKeys.rcUrl.tr(context: context), _selectedPreset!.rcBaseUrl),
|
|
|
|
Divider(height: 20.h),
|
|
|
|
Divider(height: 20.h),
|
|
|
|
_buildDetailRow(LocaleKeys.pharmacyApi.tr(context: context), _selectedPreset!.hmgPharmacyApiBaseUrl),
|
|
|
|
_buildDetailRow('Payment URL', _selectedPreset!.serviceUrl),
|
|
|
|
Divider(height: 20.h),
|
|
|
|
Divider(height: 20.h),
|
|
|
|
_buildDetailRow(
|
|
|
|
_buildDetailRow(
|
|
|
|
LocaleKeys.payfortMode.tr(context: context),
|
|
|
|
LocaleKeys.payfortMode.tr(context: context),
|
|
|
|
@ -273,7 +437,7 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
text: LocaleKeys.saveAndRestart.tr(context: context),
|
|
|
|
text: LocaleKeys.saveAndRestart.tr(context: context),
|
|
|
|
icon: AppAssets.confirm,
|
|
|
|
icon: AppAssets.confirm,
|
|
|
|
iconSize: 20.w,
|
|
|
|
iconSize: 20.w,
|
|
|
|
onPressed: _selectedPreset != null ? _saveAndRestart : null,
|
|
|
|
onPressed: (_selectedPreset != null || _isCustomMode) ? _saveAndRestart : null,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
@ -323,10 +487,35 @@ class _EnvironmentConfigSheetState extends State<EnvironmentConfigSheet> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _saveAndRestart() async {
|
|
|
|
Future<void> _saveAndRestart() async {
|
|
|
|
if (_selectedPreset == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (_isCustomMode) {
|
|
|
|
|
|
|
|
// Validate custom URLs
|
|
|
|
|
|
|
|
if (_baseUrlController.text.trim().isEmpty ||
|
|
|
|
|
|
|
|
_rcBaseUrlController.text.trim().isEmpty ||
|
|
|
|
|
|
|
|
_paymentUrlController.text.trim().isEmpty) {
|
|
|
|
|
|
|
|
Utils.showToast('Please enter all custom URLs');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure URLs end properly (payment URL should NOT have trailing /)
|
|
|
|
|
|
|
|
String baseUrl = _baseUrlController.text.trim();
|
|
|
|
|
|
|
|
String rcBaseUrl = _rcBaseUrlController.text.trim();
|
|
|
|
|
|
|
|
String paymentUrl = _paymentUrlController.text.trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!baseUrl.endsWith('/')) baseUrl += '/';
|
|
|
|
|
|
|
|
if (!rcBaseUrl.endsWith('/')) rcBaseUrl += '/';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await EnvironmentConfigService.instance.applyCustomUrls(
|
|
|
|
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
|
|
|
|
rcBaseUrl: rcBaseUrl,
|
|
|
|
|
|
|
|
paymentUrl: paymentUrl,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else if (_selectedPreset != null) {
|
|
|
|
await EnvironmentConfigService.instance.applyPreset(_selectedPreset!);
|
|
|
|
await EnvironmentConfigService.instance.applyPreset(_selectedPreset!);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Navigator.pop(context, true); // Return true to indicate app should restart
|
|
|
|
Navigator.pop(context, true); // Return true to indicate app should restart
|
|
|
|
Utils.showToast(LocaleKeys.envUpdateSuccess.tr(context: context));
|
|
|
|
Utils.showToast(LocaleKeys.envUpdateSuccess.tr(context: context));
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
|