diff --git a/assets/images/patient_register.png b/assets/images/patient_register.png new file mode 100644 index 00000000..c55935bc Binary files /dev/null and b/assets/images/patient_register.png differ diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index b711124f..93c3a2de 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -710,5 +710,7 @@ const Map> localizedValues = { "ar":"نوع الطلب"}, "special": {"en": "Special", "ar": "خاص"}, "allClinic": {"en": "All Clinics", "ar": "جميع العيادات"} , - "operationReports": {"en": "Operation Reports", "ar": "تقارير العملية"} + "operationReports": {"en": "Operation Reports", "ar": "تقارير العملية"}, + "registerNewPatient": {"en": "Register\nNew Patient", "ar": "تسجيل\n مريض جديد"}, + "registeraPatient": {"en": "Register a Patient", "ar": "تسجيل المريض"}, }; diff --git a/lib/core/service/patient/PatientRegisterService.dart b/lib/core/service/patient/PatientRegisterService.dart new file mode 100644 index 00000000..a7013a3c --- /dev/null +++ b/lib/core/service/patient/PatientRegisterService.dart @@ -0,0 +1,5 @@ +import 'package:doctor_app_flutter/core/service/base/base_service.dart'; + +class PatientRegisterService extends BaseService{ + +} \ No newline at end of file diff --git a/lib/core/viewModel/PatientRegisterViewModel.dart b/lib/core/viewModel/PatientRegisterViewModel.dart new file mode 100644 index 00000000..43aa7500 --- /dev/null +++ b/lib/core/viewModel/PatientRegisterViewModel.dart @@ -0,0 +1,8 @@ +import 'package:doctor_app_flutter/core/service/patient/PatientRegisterService.dart'; +import 'package:doctor_app_flutter/core/viewModel/base_view_model.dart'; + +import '../../locator.dart'; + +class PatientRegisterViewModel extends BaseViewModel { + PatientRegisterService _service = locator(); +} \ No newline at end of file diff --git a/lib/locator.dart b/lib/locator.dart index b5f651eb..04266041 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -23,6 +23,7 @@ import 'core/service/patient/DischargedPatientService.dart'; import 'core/service/patient/LiveCarePatientServices.dart'; import 'core/service/patient/MyReferralPatientService.dart'; import 'core/service/patient/PatientMuseService.dart'; +import 'core/service/patient/PatientRegisterService.dart'; import 'core/service/patient/ReferralService.dart'; import 'core/service/patient/out_patient_service.dart'; import 'core/service/patient/patient-doctor-referral-service.dart'; @@ -49,6 +50,7 @@ import 'core/viewModel/InsuranceViewModel.dart'; import 'core/viewModel/LiveCarePatientViewModel.dart'; import 'core/viewModel/PatientMedicalReportViewModel.dart'; import 'core/viewModel/PatientMuseViewModel.dart'; +import 'core/viewModel/PatientRegisterViewModel.dart'; import 'core/viewModel/PatientSearchViewModel.dart'; import 'core/viewModel/SOAP_view_model.dart'; import 'core/viewModel/doctor_replay_view_model.dart'; @@ -103,6 +105,7 @@ void setupLocator() { locator.registerLazySingleton(() => VideoCallService()); locator.registerLazySingleton(() => AnalyticsService()); locator.registerLazySingleton(() => OperationReportService()); + locator.registerLazySingleton(() => PatientRegisterService()); /// View Model locator.registerFactory(() => DoctorReplayViewModel()); @@ -132,4 +135,5 @@ void setupLocator() { locator.registerFactory(() => PatientMedicalReportViewModel()); locator.registerFactory(() => ScanQrViewModel()); locator.registerFactory(() => OperationReportViewModel()); + locator.registerFactory(() => PatientRegisterViewModel()); } diff --git a/lib/screens/home/home_patient_card.dart b/lib/screens/home/home_patient_card.dart index b388a7e2..a0d0bce7 100644 --- a/lib/screens/home/home_patient_card.dart +++ b/lib/screens/home/home_patient_card.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; class HomePatientCard extends StatelessWidget { final Color backgroundColor; final IconData cardIcon; + final String cardIconImage; final Color backgroundIconColor; final String text; final Color textColor; @@ -15,11 +16,12 @@ class HomePatientCard extends StatelessWidget { HomePatientCard({ @required this.backgroundColor, @required this.backgroundIconColor, - @required this.cardIcon, + this.cardIcon, + this.cardIconImage, @required this.text, @required this.textColor, @required this.onTap, - this.iconSize = 30, + this.iconSize = 30, }); @override @@ -38,24 +40,41 @@ class HomePatientCard extends StatelessWidget { children: [ Container( margin: EdgeInsets.only(top: 18, left: 10), - color:Colors.transparent, - - child: Icon( - cardIcon, - size: iconSize * 2, - color: backgroundIconColor, - ), + color: Colors.transparent, + child: cardIcon != null + ? Icon( + cardIcon, + size: iconSize * 2, + color: backgroundIconColor, + ) + : IconButton( + icon: Image.asset( + 'assets/images/patient_register.png', + width: iconSize * 2, + height: iconSize * 2, + fit: BoxFit.fill, + ), + iconSize: iconSize * 2, + color: backgroundIconColor, + onPressed: () => null, + ), ), Container( child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon( - cardIcon, - size: iconSize, - color: textColor, - ), + cardIcon != null + ? Icon( + cardIcon, + size: iconSize, + color: textColor, + ) + : Image.asset( + cardIconImage, + height: iconSize, + width: iconSize, + ), SizedBox( height: 4, ), diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 495a70b0..1ab61717 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -16,6 +16,7 @@ import 'package:doctor_app_flutter/screens/patients/In_patient/in_patient_screen import 'package:doctor_app_flutter/screens/patients/out_patient/out_patient_screen.dart'; import 'package:doctor_app_flutter/screens/patients/patient_search/patient_search_screen.dart'; import 'package:doctor_app_flutter/screens/patients/profile/referral/patient_referral_screen.dart'; +import 'package:doctor_app_flutter/screens/patients/register_patient/RegisterPatientPage.dart'; import 'package:doctor_app_flutter/util/date-utils.dart'; import 'package:doctor_app_flutter/util/dr_app_toast_msg.dart'; import 'package:doctor_app_flutter/util/translations_delegate_base.dart'; @@ -254,8 +255,8 @@ class _HomeScreenState extends State { else Container( child: ErrorMessage( - error: model.error, - )), + error: model.error, + )), FractionallySizedBox( // widthFactor: 0.90, child: Container( @@ -313,7 +314,6 @@ class _HomeScreenState extends State { ), ) ]), - ]), ), ); @@ -396,6 +396,23 @@ class _HomeScreenState extends State { )); changeColorIndex(); + patientCards.add(HomePatientCard( + backgroundColor: backgroundColors[colorIndex], + backgroundIconColor: backgroundIconColors[colorIndex], + cardIconImage: 'assets/images/patient_register.png', + textColor: textColors[colorIndex], + text: TranslationBase.of(context).registerNewPatient, + onTap: () { + Navigator.push( + context, + FadePage( + page: RegisterPatientPage(), + ), + ); + }, + )); + changeColorIndex(); + patientCards.add(HomePatientCard( backgroundColor: backgroundColors[colorIndex], backgroundIconColor: backgroundIconColors[colorIndex], diff --git a/lib/screens/patients/profile/UCAF/page-stepper-widget.dart b/lib/screens/patients/profile/UCAF/page-stepper-widget.dart index da0381ed..6d9838f4 100644 --- a/lib/screens/patients/profile/UCAF/page-stepper-widget.dart +++ b/lib/screens/patients/profile/UCAF/page-stepper-widget.dart @@ -16,8 +16,9 @@ class PageStepperWidget extends StatelessWidget { final int stepsCount; final int currentStepIndex; final Size screenSize; + final List stepsTitles; - PageStepperWidget({this.stepsCount, this.currentStepIndex, this.screenSize}); + PageStepperWidget({this.stepsCount, this.currentStepIndex, this.screenSize, this.stepsTitles}); @override Widget build(BuildContext context) { @@ -33,10 +34,10 @@ class PageStepperWidget extends StatelessWidget { for (int i = 1; i <= stepsCount; i++) if (i == currentStepIndex) StepWidget(i, true, i == stepsCount, i < currentStepIndex, - dividerWidth) + dividerWidth, stepsTitles: stepsTitles,) else StepWidget(i, false, i == stepsCount, i < currentStepIndex, - dividerWidth) + dividerWidth, stepsTitles: stepsTitles,) ], ) ], @@ -52,9 +53,10 @@ class StepWidget extends StatelessWidget { final bool isFinalStep; final bool isStepFinish; final double dividerWidth; + final List stepsTitles; StepWidget(this.index, this.isInProgress, this.isFinalStep, this.isStepFinish, - this.dividerWidth); + this.dividerWidth, {this.stepsTitles}); @override Widget build(BuildContext context) { @@ -106,7 +108,7 @@ class StepWidget extends StatelessWidget { height: 8, ), AppText( - "${TranslationBase.of(context).step} $index", + stepsTitles == null ? "${TranslationBase.of(context).step} $index" : "${stepsTitles[index - 1]}", fontWeight: FontWeight.bold, color: status == StepStatus.Locked ? Color(0xFF969696) : Colors.black, fontFamily: 'Poppins', diff --git a/lib/screens/patients/register_patient/RegisterPatientPage.dart b/lib/screens/patients/register_patient/RegisterPatientPage.dart new file mode 100644 index 00000000..ab85d820 --- /dev/null +++ b/lib/screens/patients/register_patient/RegisterPatientPage.dart @@ -0,0 +1,212 @@ +import 'package:doctor_app_flutter/core/viewModel/PatientRegisterViewModel.dart'; +import 'package:doctor_app_flutter/screens/base/base_view.dart'; +import 'package:doctor_app_flutter/screens/patients/In_patient/InPatientHeader.dart'; +import 'package:doctor_app_flutter/screens/patients/patient_search/patient_search_header.dart'; +import 'package:doctor_app_flutter/screens/patients/profile/UCAF/page-stepper-widget.dart'; +import 'package:doctor_app_flutter/util/translations_delegate_base.dart'; +import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart'; +import 'package:doctor_app_flutter/widgets/shared/buttons/app_buttons_widget.dart'; +import 'package:flutter/material.dart'; + +import 'RegisterSearchPatientPage.dart'; + +class RegisterPatientPage extends StatefulWidget { + const RegisterPatientPage({Key key}) : super(key: key); + + @override + _RegisterPatientPageState createState() => _RegisterPatientPageState(); +} + +class _RegisterPatientPageState extends State + with TickerProviderStateMixin { + PageController _controller; + int _currentIndex = 0; + bool _isLoading = false; + + changePageViewIndex(pageIndex, {isChangeState = true}) { + if (pageIndex != _currentIndex && isChangeState) changeLoadingState(true); + _controller.jumpToPage(pageIndex); + setState(() { + _currentIndex = pageIndex; + }); + } + + void changeLoadingState(bool isLoading) { + setState(() { + _isLoading = isLoading; + }); + } + + @override + void initState() { + _controller = new PageController(); + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + + return BaseView( + builder: (_, model, w) => AppScaffold( + baseViewModel: model, + isShowAppBar: true, + isLoading: _isLoading, + appBar: PatientSearchHeader( + title: TranslationBase.of(context).registeraPatient, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 10, + ), + PageStepperWidget( + stepsCount: 3, + currentStepIndex: _currentIndex + 1, + screenSize: screenSize, + stepsTitles: [ + "Search", + "Activation", + "Confirmation", + ], + ), + SizedBox( + height: 10, + ), + Expanded( + child: Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: PageView( + physics: NeverScrollableScrollPhysics(), + controller: _controller, + onPageChanged: (index) { + setState(() { + _currentIndex = index; + }); + }, + scrollDirection: Axis.horizontal, + children: [ + RegisterSearchPatientPage(), + ]), + ), + ), + ], + ), + )), + _isLoading + ? Container( + height: 0, + ) + : pagerButtons(model), + ], + ), + ), + ); + } + + Widget pagerButtons(PatientRegisterViewModel model) { + switch (_currentIndex) { + case 2: + return Container( + margin: EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: Row( + children: [ + Expanded( + child: Container( + child: AppButton( + title: TranslationBase.of(context).cancel, + hasBorder: true, + vPadding: 12, + hPadding: 8, + borderColor: Color(0xFFeaeaea), + color: Color(0xFFeaeaea), + fontColor: Colors.black, + fontSize: 2.2, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + ), + SizedBox( + width: 8, + ), + Expanded( + child: Container( + child: AppButton( + title: TranslationBase.of(context).noteConfirm, + hasBorder: true, + vPadding: 12, + hPadding: 8, + borderColor: Color(0xFF359846), + color: Color(0xFF359846), + fontColor: Colors.white, + fontSize: 2.0, + onPressed: () {}, + ), + ), + ), + ], + ), + ); + default: + return Container( + color: Colors.white, + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: Row( + children: [ + Expanded( + child: Container( + child: AppButton( + title: TranslationBase.of(context).cancel, + hasBorder: true, + vPadding: 12, + hPadding: 8, + borderColor: Color(0xFFeaeaea), + color: Color(0xFFeaeaea), + fontColor: Colors.black, + fontSize: 2.2, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + ), + SizedBox( + width: 8, + ), + Expanded( + child: Container( + child: AppButton( + title: TranslationBase.of(context).next, + hasBorder: true, + vPadding: 12, + hPadding: 8, + borderColor: Color(0xFFB8382B), + color: Color(0xFFB8382B), + fontColor: Colors.white, + fontSize: 2.0, + onPressed: () { + changePageViewIndex(_currentIndex + 1); + }, + ), + ), + ), + ], + ), + ); + } + } +} diff --git a/lib/screens/patients/register_patient/RegisterSearchPatientPage.dart b/lib/screens/patients/register_patient/RegisterSearchPatientPage.dart new file mode 100644 index 00000000..3aafe5bd --- /dev/null +++ b/lib/screens/patients/register_patient/RegisterSearchPatientPage.dart @@ -0,0 +1,201 @@ +import 'package:doctor_app_flutter/config/size_config.dart'; +import 'package:doctor_app_flutter/core/enum/viewstate.dart'; +import 'package:doctor_app_flutter/core/viewModel/PatientRegisterViewModel.dart'; +import 'package:doctor_app_flutter/screens/base/base_view.dart'; +import 'package:doctor_app_flutter/util/date-utils.dart'; +import 'package:doctor_app_flutter/util/dr_app_toast_msg.dart'; +import 'package:doctor_app_flutter/util/translations_delegate_base.dart'; +import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart'; +import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart'; +import 'package:doctor_app_flutter/widgets/shared/dialogs/dailog-list-select.dart'; +import 'package:doctor_app_flutter/widgets/shared/loader/gif_loader_dialog_utils.dart'; +import 'package:doctor_app_flutter/widgets/shared/text_fields/app-textfield-custom.dart'; +import 'package:flutter/material.dart'; + +class RegisterSearchPatientPage extends StatefulWidget { + const RegisterSearchPatientPage({Key key}) : super(key: key); + + @override + _RegisterSearchPatientPageState createState() => + _RegisterSearchPatientPageState(); +} + +class _RegisterSearchPatientPageState extends State { + String countryError; + dynamic _selectedCountry; + + final _phoneController = TextEditingController(); + String phoneError; + + final _idController = TextEditingController(); + String idError; + + DateTime _birthDate; + String birthdateError; + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + + return BaseView( + builder: (_, model, w) => AppScaffold( + baseViewModel: model, + isShowAppBar: false, + body: Column( + children: [ + Expanded( + child: Container( + width: double.infinity, + margin: EdgeInsets.all(16.0), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AppText( + "Please enter mobile number or Identification number", + fontFamily: 'Poppins', + fontSize: SizeConfig.textMultiplier * 2.2, + fontWeight: FontWeight.w800, + ), + SizedBox( + height: 10, + ), + AppTextFieldCustom( + height: screenSize.height * 0.075, + hintText: "Country", + isTextFieldHasSuffix: true, + validationError: countryError, + dropDownText: _selectedCountry != null + ? _selectedCountry['nameEn'] + : null, + enabled: false, + /*onClick: model.dietTypesList != null && model.dietTypesList.length > 0 + ? () { + openListDialogField('nameEn', 'id', model.dietTypesList, (selectedValue) { + setState(() { + _selectedCountry = selectedValue; + }); + }); + } + : () async { + GifLoaderDialogUtils.showMyDialog(context); + await model + .getDietTypes(patient.patientId) + .then((_) => GifLoaderDialogUtils.hideDialog(context)); + if (model.state == ViewState.Idle && model.dietTypesList.length > 0) { + openListDialogField('nameEn', 'id', model.dietTypesList, (selectedValue) { + setState(() { + _selectedCountry = selectedValue; + }); + }); + } else if (model.state == ViewState.ErrorLocal) { + DrAppToastMsg.showErrorToast(model.error); + } else { + DrAppToastMsg.showErrorToast("Empty List"); + } + },*/ + ), + SizedBox( + height: 10, + ), + AppTextFieldCustom( + height: screenSize.height * 0.075, + hintText: "Phone Number", + inputType: TextInputType.phone, + controller: _phoneController, + validationError: phoneError, + ), + SizedBox( + height: 10, + ), + AppTextFieldCustom( + height: screenSize.height * 0.075, + hintText: "ID Number", + inputType: TextInputType.phone, + controller: _idController, + validationError: idError, + ), + SizedBox( + height: 12, + ), + AppText( + "Calender", + fontSize: SizeConfig.textMultiplier * 1.8, + fontWeight: FontWeight.w800, + ), + SizedBox( + height: 10, + ), + AppTextFieldCustom( + height: screenSize.height * 0.075, + hintText: "Birthdate", + dropDownText: _birthDate != null + ? "${AppDateUtils.convertStringToDateFormat(_birthDate.toString(), "yyyy-MM-dd")}" + : null, + enabled: false, + isTextFieldHasSuffix: true, + validationError: birthdateError, + suffixIcon: IconButton( + icon: Icon( + Icons.calendar_today, + color: Colors.black, + ), + onPressed: null, + ), + onClick: () { + if (_birthDate == null) { + _birthDate = DateTime.now(); + } + _selectDate(context, _birthDate, (picked) { + setState(() { + _birthDate = picked; + }); + }); + }, + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } + + Future _selectDate(BuildContext context, DateTime dateTime, + Function(DateTime picked) updateDate) async { + final DateTime picked = await showDatePicker( + context: context, + initialDate: dateTime, + firstDate: DateTime.now(), + lastDate: DateTime(2040), + initialEntryMode: DatePickerEntryMode.calendar, + ); + if (picked != null && picked != dateTime) { + updateDate(picked); + } + } + + void openListDialogField(String attributeName, String attributeValueId, + List list, Function(dynamic selectedValue) okFunction) { + ListSelectDialog dialog = ListSelectDialog( + list: list, + attributeName: attributeName, + attributeValueId: attributeValueId, + usingSearch: true, + okText: TranslationBase.of(context).ok, + okFunction: (selectedValue) { + okFunction(selectedValue); + }, + ); + showDialog( + barrierDismissible: false, + context: context, + builder: (BuildContext context) { + return dialog; + }, + ); + } +} diff --git a/lib/util/translations_delegate_base.dart b/lib/util/translations_delegate_base.dart index 59f66811..58e17a94 100644 --- a/lib/util/translations_delegate_base.dart +++ b/lib/util/translations_delegate_base.dart @@ -1106,6 +1106,8 @@ class TranslationBase { String get requestType => localizedValues['requestType'][locale.languageCode]; String get allClinic => localizedValues['allClinic'][locale.languageCode]; String get notReplied => localizedValues['notReplied'][locale.languageCode]; + String get registerNewPatient => localizedValues['registerNewPatient'][locale.languageCode]; + String get registeraPatient => localizedValues['registeraPatient'][locale.languageCode]; } diff --git a/lib/widgets/shared/text_fields/app-textfield-custom.dart b/lib/widgets/shared/text_fields/app-textfield-custom.dart index cd97c8d4..086375bb 100644 --- a/lib/widgets/shared/text_fields/app-textfield-custom.dart +++ b/lib/widgets/shared/text_fields/app-textfield-custom.dart @@ -146,6 +146,7 @@ class _AppTextFieldCustomState extends State { ? TextAlign.right : TextAlign.left, focusNode: _focusNode, + textAlignVertical: TextAlignVertical.center, decoration: TextFieldsUtils .textFieldSelectorDecoration( widget.hintText, null, true),