SSO and chatbot
This commit is contained in:
parent
a157694897
commit
ab44ad5815
@ -19,12 +19,12 @@ if (project.hasProperty('google-services.json')) {
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '63'
|
||||
flutterVersionCode = '65'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '2.0.25'
|
||||
flutterVersionName = '2.0.27'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
|
||||
@ -5,7 +5,7 @@ class Environment {
|
||||
static Flavor flavor = Flavor.dev; // overwritten by each main_*.dart
|
||||
|
||||
/// Chatbot FAB + window. Set to `true` when ready to enable.
|
||||
static const bool chatbotEnabled = true;
|
||||
static const bool chatbotEnabled = false;
|
||||
|
||||
static bool get isProd => flavor == Flavor.prod || flavor == Flavor.prod1;
|
||||
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:nhance_app_pwa/features/chatbot/data/chatbot_api_service.dart';
|
||||
import 'package:nhance_app_pwa/features/chatbot/data/chatbot_repository.dart';
|
||||
import 'package:nhance_app_pwa/features/chatbot/domain/models/chatbot_models.dart';
|
||||
import 'package:nhance_app_pwa/pages/helpers/ecard_download_notification_service.dart';
|
||||
import 'package:nhance_app_pwa/pages/helpers/ecard_download_service.dart';
|
||||
import 'package:nhance_app_pwa/pages/service/TokenService.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final chatbotRepositoryProvider = Provider<ChatbotRepository>((ref) {
|
||||
@ -98,6 +102,7 @@ class ChatbotUiState {
|
||||
|
||||
class ChatbotController extends StateNotifier<ChatbotUiState> {
|
||||
final ChatbotRepository _repository;
|
||||
final EcardDownloadService _ecardDownloadService = const EcardDownloadService();
|
||||
List<ChatbotPolicy>? _policiesCache;
|
||||
DateTime? _policiesCacheAt;
|
||||
static const Duration _cacheTtl = Duration(minutes: 10);
|
||||
@ -201,7 +206,7 @@ class ChatbotController extends StateNotifier<ChatbotUiState> {
|
||||
showCoveredMembers();
|
||||
return;
|
||||
case 'ecard_download':
|
||||
await openEcard();
|
||||
await openEcard(context);
|
||||
return;
|
||||
case 'covered_hospital_list':
|
||||
await openHospitalList();
|
||||
@ -298,19 +303,31 @@ class ChatbotController extends StateNotifier<ChatbotUiState> {
|
||||
|
||||
void selectPolicy(ChatbotPolicy policy) {
|
||||
_appendBotMessage('Selected ${policy.policyName}.');
|
||||
final isGmcPolicy = _isGmcPolicy(policy);
|
||||
final actionOptions = <ChatbotOption>[
|
||||
const ChatbotOption(id: 'covered_member_list', label: 'Covered Member List'),
|
||||
if (isGmcPolicy)
|
||||
const ChatbotOption(id: 'ecard_download', label: 'E-Card Download'),
|
||||
if (isGmcPolicy)
|
||||
const ChatbotOption(
|
||||
id: 'covered_hospital_list',
|
||||
label: 'Covered Hospital List',
|
||||
),
|
||||
const ChatbotOption(id: 'back_to_policies', label: 'Back to Policies'),
|
||||
];
|
||||
state = state.copyWith(
|
||||
selectedPolicy: policy,
|
||||
step: ChatbotFlowStep.policyActions,
|
||||
options: const [
|
||||
ChatbotOption(id: 'covered_member_list', label: 'Covered Member List'),
|
||||
ChatbotOption(id: 'ecard_download', label: 'E-Card Download'),
|
||||
ChatbotOption(id: 'covered_hospital_list', label: 'Covered Hospital List'),
|
||||
ChatbotOption(id: 'back_to_policies', label: 'Back to Policies'),
|
||||
],
|
||||
options: actionOptions,
|
||||
clearError: true,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isGmcPolicy(ChatbotPolicy policy) {
|
||||
final normalizedType = policy.policyType.trim().toUpperCase();
|
||||
return normalizedType.startsWith('GMC');
|
||||
}
|
||||
|
||||
void showCoveredMembers() {
|
||||
final policy = state.selectedPolicy;
|
||||
if (policy == null) {
|
||||
@ -328,7 +345,7 @@ class ChatbotController extends StateNotifier<ChatbotUiState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openEcard() async {
|
||||
Future<void> openEcard(BuildContext context) async {
|
||||
final policy = state.selectedPolicy;
|
||||
if (policy == null) {
|
||||
_appendBotMessage('Please choose a policy first.');
|
||||
@ -347,12 +364,43 @@ class ChatbotController extends StateNotifier<ChatbotUiState> {
|
||||
return;
|
||||
}
|
||||
|
||||
final launched = await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
if (!launched) {
|
||||
_appendBotMessage('Unable to open E-Card link.');
|
||||
final fileName = _chatbotEcardFileName(policy);
|
||||
final postToken = await TokenService.getPostToken();
|
||||
final headers = <String, String>{
|
||||
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
if (postToken != null && postToken.isNotEmpty)
|
||||
'Authorization': 'Bearer $postToken',
|
||||
};
|
||||
|
||||
if (!kIsWeb) {
|
||||
_appendBotMessage('Downloading eCard...');
|
||||
}
|
||||
|
||||
final result = await _ecardDownloadService.downloadEcard(
|
||||
url: uri.toString(),
|
||||
fileName: fileName,
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
_appendBotMessage(result.message ?? 'Unable to download E-Card.');
|
||||
return;
|
||||
}
|
||||
_appendBotMessage('Opened E-Card in browser.');
|
||||
|
||||
if (!kIsWeb && result.savedPath != null && result.savedPath!.isNotEmpty) {
|
||||
await EcardDownloadNotificationService.showDownloadCompleted(
|
||||
filePath: result.savedPath!,
|
||||
fileName: fileName,
|
||||
);
|
||||
}
|
||||
|
||||
_appendBotMessage(result.message ?? 'E-Card downloaded successfully.');
|
||||
}
|
||||
|
||||
String _chatbotEcardFileName(ChatbotPolicy policy) {
|
||||
final sanitized = policy.clientPolicyId.replaceAll(RegExp(r'[^\w\-.]'), '_');
|
||||
if (sanitized.isEmpty) return 'ecard.pdf';
|
||||
return 'ecard_$sanitized.pdf';
|
||||
}
|
||||
|
||||
Future<void> openHospitalList() async {
|
||||
|
||||
@ -21,6 +21,7 @@ class PolicyMember {
|
||||
class ChatbotPolicy {
|
||||
final String clientPolicyId;
|
||||
final String policyName;
|
||||
final String policyType;
|
||||
final String eCardDownloadUrl;
|
||||
final String networkHospitalsUrl;
|
||||
final List<PolicyMember> members;
|
||||
@ -28,6 +29,7 @@ class ChatbotPolicy {
|
||||
const ChatbotPolicy({
|
||||
required this.clientPolicyId,
|
||||
required this.policyName,
|
||||
required this.policyType,
|
||||
required this.eCardDownloadUrl,
|
||||
required this.networkHospitalsUrl,
|
||||
required this.members,
|
||||
@ -38,6 +40,8 @@ class ChatbotPolicy {
|
||||
return ChatbotPolicy(
|
||||
clientPolicyId: (json['client_policy_id'] ?? '').toString(),
|
||||
policyName: (json['heading'] ?? 'Unnamed Policy').toString(),
|
||||
policyType:
|
||||
(json['policy_type'] ?? json['policy_type_long_name'] ?? '').toString(),
|
||||
eCardDownloadUrl: (json['eCardDownload'] ?? '').toString(),
|
||||
networkHospitalsUrl: (json['network_hospitals_url'] ?? '').toString(),
|
||||
members: membersRaw
|
||||
|
||||
@ -126,10 +126,12 @@ Future<DownloadResult> _downloadAndShareOnIos({
|
||||
return DownloadResult.failure('Download failed');
|
||||
}
|
||||
|
||||
await Share.shareXFiles(
|
||||
[XFile(filePath)],
|
||||
text: 'Save eCard to Files',
|
||||
subject: safeFileName,
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
files: [XFile(filePath)],
|
||||
text: 'Save eCard to Files',
|
||||
subject: safeFileName,
|
||||
),
|
||||
);
|
||||
|
||||
return DownloadResult.success(
|
||||
|
||||
@ -1877,64 +1877,63 @@ class _loginState extends State<login> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
margin: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isSsoButtonEnabled &&
|
||||
!_isLoadingSAML
|
||||
? _handleSsoLoginButtonTap
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
disabledBackgroundColor:
|
||||
const Color(0xFFF5F5F5),
|
||||
disabledForegroundColor:
|
||||
Colors.black38,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 5,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
side: BorderSide(
|
||||
color: _isSsoButtonEnabled
|
||||
? Colors.black
|
||||
: Colors.black26,
|
||||
width: 0.5,
|
||||
if (_isSsoButtonEnabled) ...[
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
margin: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45,
|
||||
child: ElevatedButton(
|
||||
onPressed: !_isLoadingSAML
|
||||
? _handleSsoLoginButtonTap
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
disabledBackgroundColor:
|
||||
const Color(0xFFF5F5F5),
|
||||
disabledForegroundColor:
|
||||
Colors.black38,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Sign In With Microsoft",
|
||||
style: GoogleFonts.poppins(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 13,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
side: const BorderSide(
|
||||
color: Colors.black,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Image.asset(
|
||||
'assets/images/login/microsoft.png',
|
||||
width: 22,
|
||||
height: 22,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Sign In With Microsoft",
|
||||
style: GoogleFonts.poppins(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Image.asset(
|
||||
'assets/images/login/microsoft.png',
|
||||
width: 22,
|
||||
height: 22,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
SizedBox(height: 15),
|
||||
],
|
||||
// MouseRegion(
|
||||
// cursor:
|
||||
// SystemMouseCursors.click,
|
||||
|
||||
@ -17,8 +17,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
#version: 1.2.41+98
|
||||
version: 1.0.37+43
|
||||
#version: 2.0.25+63
|
||||
version: 1.0.38+44
|
||||
#version: 2.0.27+65
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.3 <4.0.0'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user