diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..335c9ac692afbf60fa9dc19c994271d988402667 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "smf-mobile", + "request": "launch", + "type": "dart" + }, + { + "name": "smf-mobile (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "smf-mobile (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index dd27d612675da7ca8b6ef9d8ad73a955d12afbbd..8f19391d578b8b63223214d9712092d770b32867 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -50,6 +50,8 @@ "inspetionCompletedOn": "Assessment completed on", "inspectionSummary": "Assessment Summary", "leadInspector": "Lead assessor", + "firstLeadAssessor": "First Lead assessor", + "secondLeadAssessor": "Second Lead assessor", "assistingInspectors": "Assisting assessor", "iDisagree": "I disagree", "iConsent": "I consent", @@ -68,5 +70,6 @@ "remove": "Remove", "attachment": "Attachment", "version": "Version", - "canNotSubmitEmptyForm": "Empty assessment can't be submitted" + "canNotSubmitEmptyFormError": "Empty assessment can't be submitted", + "assessmentForAllError": "Assessment is not given for all fields" } \ No newline at end of file diff --git a/lib/models/application_model.dart b/lib/models/application_model.dart index 305ca68e352b75db9fd97c6ebd668bebfcbf05ff..6e531c7b9ae2495879bf93809202f72874a4cdf8 100644 --- a/lib/models/application_model.dart +++ b/lib/models/application_model.dart @@ -7,12 +7,14 @@ class Application { final Map dataObject; final List inspectors; final List leadInspector; + final List assistingInspector; // using it as second lead assessor final Map inspectorDataObject; final Map inspectorSummaryDataObject; final String inspectionStatus; final String scheduledDate; final String createdDate; final String createdBy; + final String updatedBy; const Application({ required this.formId, @@ -23,12 +25,14 @@ class Application { required this.dataObject, required this.inspectors, required this.leadInspector, + required this.assistingInspector, required this.inspectorDataObject, required this.inspectorSummaryDataObject, required this.inspectionStatus, required this.scheduledDate, required this.createdDate, required this.createdBy, + required this.updatedBy, }); factory Application.fromJson(Map<String, dynamic> json) { @@ -43,6 +47,9 @@ class Application { json['inspection'] != null ? json['inspection']['assignedTo'] : [], leadInspector: json['inspection'] != null ? json['inspection']['leadInspector'] : [], + assistingInspector: json['inspection'] != null + ? json['inspection']['assistingInspector'] + : [], inspectorDataObject: json['inspectorDataObject'] != null ? json['inspectorDataObject']['dataObject'] : {}, @@ -53,6 +60,7 @@ class Application { json['inspection'] != null ? json['inspection']['scheduledDate'] : '', createdDate: json['createdDate'], createdBy: json['createdBy'], + updatedBy: json['updatedBy'] ?? "", ); } @@ -65,6 +73,7 @@ class Application { dataObject, inspectors, leadInspector, + assistingInspector, inspectorDataObject, inspectorSummaryDataObject, inspectionStatus, diff --git a/lib/pages/application_details_page.dart b/lib/pages/application_details_page.dart index 849d09197518d7ad9633971b28b25bb5905333ef..7df209d9bd810ab178dfccf559bf3c11a857299f 100644 --- a/lib/pages/application_details_page.dart +++ b/lib/pages/application_details_page.dart @@ -26,9 +26,11 @@ import 'package:smf_mobile/services/location_service.dart'; class ApplicationDetailsPage extends StatefulWidget { final Application application; + final bool isPastApplication; const ApplicationDetailsPage({ Key? key, required this.application, + this.isPastApplication = false, }) : super(key: key); @override @@ -86,12 +88,15 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> _userId = int.parse(id); if (widget.application.leadInspector.isNotEmpty) { _leadInspectorId = widget.application.leadInspector[0]; - if (widget.application.leadInspector[0] == _userId) { + if (widget.application.leadInspector[0] == _userId || + widget.application.assistingInspector[0] == _userId) { setState(() { _isleadInspector = true; }); - } else if (widget.application.status == - InspectionStatus.inspectionCompleted) { + } + if (widget.application.status == InspectionStatus.inspectionCompleted || + widget.application.inspectionStatus == + InspectionStatus.leadInspectorCompleted) { _inspectionSummary = widget.application.inspectorSummaryDataObject['Inspection Summary'][ widget.application @@ -286,20 +291,43 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> 'longitude': position.longitude, }; - //Validate assessment form to make sure some inputs are given by the assessor int fieldsLength = _data.keys.length; bool isValidForm = false; - for (int i = 0; i < fieldsLength; i++) { - _data[_data.keys.elementAt(i)].forEach((key, value) { - if (value[value.keys.elementAt(0)]["value"].isNotEmpty) { - isValidForm = true; - } - }); + //Validate assessment form to make sure all inputs are given by the assessor when second assessor submmits the form + if (widget.application.inspectionStatus == + InspectionStatus.leadInspectorCompleted) { + data["inspectionCompleted"] = true; + isValidForm = true; + for (int i = 0; i < fieldsLength; i++) { + _data[_data.keys.elementAt(i)].forEach((key, value) { + if (value[value.keys.elementAt(0)]["value"] == null || + value[value.keys.elementAt(0)]["value"]?.isEmpty) { + isValidForm = false; + } + }); + } + } else { + //Validate assessment form to make sure some inputs are given by the assessor when first assessor submmits + for (int i = 0; i < fieldsLength; i++) { + _data[_data.keys.elementAt(i)].forEach((key, value) { + if (value[value.keys.elementAt(0)]["value"]?.isNotEmpty) { + isValidForm = true; + } + }); + } } if (!isValidForm) { - Helper.toastMessage("Empty assessment can't be submitted"); - return; + if (widget.application.inspectionStatus == + InspectionStatus.leadInspectorCompleted) { + Helper.toastMessage( + AppLocalizations.of(context)!.assessmentForAllError); + return; + } else { + Helper.toastMessage( + AppLocalizations.of(context)!.canNotSubmitEmptyFormError); + return; + } } Navigator.of(context).pushReplacement(MaterialPageRoute( @@ -466,7 +494,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> children: [ for (Map field in _fields) ListView(children: [ - !_isleadInspector + widget.isPastApplication ? Wrap( children: [ Container( @@ -548,36 +576,36 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> ), ), ), - Container( - margin: - const EdgeInsets - .only( - top: 10), - width: - double.infinity, - child: Text( - AppLocalizations.of( - context)! - .inspetionCompletedOn + - ' ' + - Helper.formatDate(widget - .application - .scheduledDate), - textAlign: TextAlign - .center, - style: GoogleFonts - .lato( - color: AppColors - .black60, - fontSize: 14.0, - letterSpacing: - 0.25, - fontWeight: - FontWeight - .w400, - ), - ), - ), + // Container( + // margin: + // const EdgeInsets + // .only( + // top: 10), + // width: + // double.infinity, + // child: Text( + // AppLocalizations.of( + // context)! + // .inspetionCompletedOn + + // ' ' + + // Helper.formatDate(widget + // .application + // .scheduledDate), + // textAlign: TextAlign + // .center, + // style: GoogleFonts + // .lato( + // color: AppColors + // .black60, + // fontSize: 14.0, + // letterSpacing: + // 0.25, + // fontWeight: + // FontWeight + // .w400, + // ), + // ), + // ), ]), ), Container( @@ -689,7 +717,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> child: Text( AppLocalizations.of( context)! - .leadInspector, + .firstLeadAssessor, style: GoogleFonts .lato( @@ -727,7 +755,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> child: Text( AppLocalizations.of( context)! - .assistingInspectors, + .secondLeadAssessor, style: GoogleFonts .lato( color: AppColors @@ -785,7 +813,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> InspectionStatus .leadInspectorCompleted ? InspectionStatus - .inspectionCompleted + .leadInspectorCompleted : widget .application.status, parentAction: updateField, @@ -848,147 +876,147 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> ), ), child: Column(children: [ - !_isleadInspector && - widget.application.inspectionStatus == - InspectionStatus.leadInspectorCompleted - ? Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - !_iConcent - ? ButtonTheme( - child: OutlinedButton( - onPressed: () { - if (!_iDisagree && - _inspectionStatus != - InspectionStatus - .inspectionCompleted) { - _displayCommentDialog(); - } - if (_inspectionStatus != - InspectionStatus - .inspectionCompleted) { - setState(() { - _iDisagree = !_iDisagree; - }); - } - }, - style: OutlinedButton.styleFrom( - backgroundColor: _iDisagree - ? AppColors.primaryBlue - : Colors.white, - side: const BorderSide( - width: 1, color: AppColors.black40), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(4), - ), - // onSurface: Colors.grey, - ), - child: Row( - children: [ - Text( - AppLocalizations.of(context)! - .iDisagree, - style: GoogleFonts.lato( - color: _iDisagree - ? Colors.white - : AppColors.black60, - fontSize: 14, - letterSpacing: 0.5, - fontWeight: FontWeight.w700), - ), - Padding( - padding: - const EdgeInsets.fromLTRB( - 5, 0, 0, 0), - child: Icon( - Icons.clear, - color: _iDisagree - ? Colors.white - : AppColors.black60, - size: 20, - )) - ], - )), - ) - : const Center(), - !_iDisagree - ? Padding( - padding: const EdgeInsets.only(left: 10), - child: TextButton( - onPressed: () { - if (!_iConcent && - _inspectionStatus != - InspectionStatus - .inspectionCompleted) { - _displayCommentDialog(); - } - if (_inspectionStatus != - InspectionStatus - .inspectionCompleted) { - setState(() { - _iConcent = !_iConcent; - }); - } - }, - style: TextButton.styleFrom( - // primary: Colors.white, - padding: const EdgeInsets.only( - left: 15, right: 15), - backgroundColor: _iConcent - ? AppColors.primaryBlue - : Colors.white, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(4), - side: const BorderSide( - color: AppColors.black40)), - ), - child: Row( - children: [ - Text( - AppLocalizations.of(context)! - .iConsent, - style: GoogleFonts.lato( - color: _iConcent - ? Colors.white - : AppColors.black60, - fontSize: 14, - letterSpacing: 0.5, - fontWeight: FontWeight.w700), - ), - Padding( - padding: - const EdgeInsets.fromLTRB( - 5, 0, 0, 0), - child: Icon( - Icons.check, - color: _iConcent - ? Colors.white - : AppColors.black60, - size: 20, - )) - ], - )), - ) - : const Center(), - _iConcent || _iDisagree - ? Padding( - padding: const EdgeInsets.only(left: 0), - child: IconButton( - onPressed: () { - _displayCommentDialog(); - }, - icon: const Icon( - Icons.edit, - color: AppColors.black40, - ), - ), - ) - : const Center() - ], - ) - : const Center(), + // !_isleadInspector && + // widget.application.inspectionStatus == + // InspectionStatus.leadInspectorCompleted + // ? Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // !_iConcent + // ? ButtonTheme( + // child: OutlinedButton( + // onPressed: () { + // if (!_iDisagree && + // _inspectionStatus != + // InspectionStatus + // .inspectionCompleted) { + // _displayCommentDialog(); + // } + // if (_inspectionStatus != + // InspectionStatus + // .inspectionCompleted) { + // setState(() { + // _iDisagree = !_iDisagree; + // }); + // } + // }, + // style: OutlinedButton.styleFrom( + // backgroundColor: _iDisagree + // ? AppColors.primaryBlue + // : Colors.white, + // side: const BorderSide( + // width: 1, color: AppColors.black40), + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(4), + // ), + // // onSurface: Colors.grey, + // ), + // child: Row( + // children: [ + // Text( + // AppLocalizations.of(context)! + // .iDisagree, + // style: GoogleFonts.lato( + // color: _iDisagree + // ? Colors.white + // : AppColors.black60, + // fontSize: 14, + // letterSpacing: 0.5, + // fontWeight: FontWeight.w700), + // ), + // Padding( + // padding: + // const EdgeInsets.fromLTRB( + // 5, 0, 0, 0), + // child: Icon( + // Icons.clear, + // color: _iDisagree + // ? Colors.white + // : AppColors.black60, + // size: 20, + // )) + // ], + // )), + // ) + // : const Center(), + // !_iDisagree + // ? Padding( + // padding: const EdgeInsets.only(left: 10), + // child: TextButton( + // onPressed: () { + // if (!_iConcent && + // _inspectionStatus != + // InspectionStatus + // .inspectionCompleted) { + // _displayCommentDialog(); + // } + // if (_inspectionStatus != + // InspectionStatus + // .inspectionCompleted) { + // setState(() { + // _iConcent = !_iConcent; + // }); + // } + // }, + // style: TextButton.styleFrom( + // // primary: Colors.white, + // padding: const EdgeInsets.only( + // left: 15, right: 15), + // backgroundColor: _iConcent + // ? AppColors.primaryBlue + // : Colors.white, + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(4), + // side: const BorderSide( + // color: AppColors.black40)), + // ), + // child: Row( + // children: [ + // Text( + // AppLocalizations.of(context)! + // .iConsent, + // style: GoogleFonts.lato( + // color: _iConcent + // ? Colors.white + // : AppColors.black60, + // fontSize: 14, + // letterSpacing: 0.5, + // fontWeight: FontWeight.w700), + // ), + // Padding( + // padding: + // const EdgeInsets.fromLTRB( + // 5, 0, 0, 0), + // child: Icon( + // Icons.check, + // color: _iConcent + // ? Colors.white + // : AppColors.black60, + // size: 20, + // )) + // ], + // )), + // ) + // : const Center(), + // _iConcent || _iDisagree + // ? Padding( + // padding: const EdgeInsets.only(left: 0), + // child: IconButton( + // onPressed: () { + // _displayCommentDialog(); + // }, + // icon: const Icon( + // Icons.edit, + // color: AppColors.black40, + // ), + // ), + // ) + // : const Center() + // ], + // ) + // : const Center(), Container( margin: const EdgeInsets.only(top: 10), child: Row( @@ -1051,13 +1079,16 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> InspectionStatus .leadInspectorCompleted && widget.application.status != - InspectionStatus.approved) || + InspectionStatus.approved && + !widget.isPastApplication) || (widget.application.inspectionStatus == InspectionStatus .leadInspectorCompleted && - !_isleadInspector && + // !_isleadInspector && widget.application.status != - InspectionStatus.inspectionCompleted) + InspectionStatus + .inspectionCompleted && + !widget.isPastApplication) ? TextButton( onPressed: () { _submitInspection(); diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index b6baa46ffdd3a09d171f94c29a56af3b539d4670..08dd71e04ff87b02ac77728dd4138caf8f4b35c4 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -128,6 +128,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver { Future<dynamic> _getApplications() async { String rawUserId = await Helper.getUser(Storage.userId); + String username = await Helper.getUser(Storage.username); int userId = int.parse(rawUserId); bool isInternetConnected = await Helper.isInternetConnected(); // await Future.delayed(const Duration(milliseconds: 10)); @@ -154,17 +155,18 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver { temp = List.from(temp.reversed); int days = Helper.getDateDiffence( DateTime.now(), DateTime.parse(temp.join("-"))); - if ((days == 0 || days > 0) && + if ((days == 0) && application.status == InspectionStatus.sentForInspection) { - if (application.leadInspector[0] == userId && - application.inspectionStatus == - InspectionStatus.leadInspectorCompleted) { + if (application.inspectionStatus == + InspectionStatus.leadInspectorCompleted && + application.updatedBy == username) { _pastApplications.add(application); } else { _pendingApplications.add(application); } - } else if ((days == 0 || days > 0) && - application.status != InspectionStatus.sentForInspection) { + } else if (((days == 0) && + application.status != InspectionStatus.sentForInspection) || + (days > 0)) { _pastApplications.add(application); } else { _upcomingApplications.add(application); diff --git a/lib/pages/inspection_summary.dart b/lib/pages/inspection_summary.dart index 6ca2d9818d0c51801694dda398cea988ffb2428c..5e55082a82f77187c733bd41ecf1073e9f5f6126 100644 --- a/lib/pages/inspection_summary.dart +++ b/lib/pages/inspection_summary.dart @@ -24,14 +24,14 @@ class InspectionSummaryPage extends StatefulWidget { final List inspectionFields; final Map inspectionData; - const InspectionSummaryPage( - {Key? key, - required this.formId, - required this.inspectors, - required this.leadInspector, - required this.inspectionFields, - required this.inspectionData}) - : super(key: key); + const InspectionSummaryPage({ + Key? key, + required this.formId, + required this.inspectors, + required this.leadInspector, + required this.inspectionFields, + required this.inspectionData, + }) : super(key: key); @override _InspectionSummaryPageState createState() => _InspectionSummaryPageState(); } @@ -40,6 +40,7 @@ class _InspectionSummaryPageState extends State<InspectionSummaryPage> { // Map _source = {ConnectivityResult.none: false}; // final MyConnectivity _connectivity = MyConnectivity.instance; final TextEditingController _summaryController = TextEditingController(); + final List<Map> _inspectors = []; int _leadInspectorId = 0; bool _iAgree = false; @@ -248,7 +249,7 @@ class _InspectionSummaryPageState extends State<InspectionSummaryPage> { ? Container( margin: const EdgeInsets.fromLTRB(20, 10, 20, 15), child: Text( - 'Lead assessor', + AppLocalizations.of(context)!.firstLeadAssessor, style: GoogleFonts.lato( color: AppColors.black87, fontWeight: FontWeight.w700, @@ -267,7 +268,7 @@ class _InspectionSummaryPageState extends State<InspectionSummaryPage> { Container( margin: const EdgeInsets.fromLTRB(20, 10, 20, 15), child: Text( - 'Assisting assessor', + AppLocalizations.of(context)!.secondLeadAssessor, style: GoogleFonts.lato( color: AppColors.black87, fontWeight: FontWeight.w700, diff --git a/lib/pages/past_applications.dart b/lib/pages/past_applications.dart index f90272308642ab7fcca7381b7f9d05f34d9c38e2..42413e2d3c923681d7cba8b25cb82c6d1f16520a 100644 --- a/lib/pages/past_applications.dart +++ b/lib/pages/past_applications.dart @@ -53,7 +53,9 @@ class _PastApplicationsState extends State<PastApplications> { itemCount: widget.pastApplications.length, itemBuilder: (context, i) { return ApplicationCard( - application: widget.pastApplications[i]); + application: widget.pastApplications[i], + isPastApplication: true, + ); }, ), ], diff --git a/lib/widgets/application_card.dart b/lib/widgets/application_card.dart index 05f40707357f4e65bd930aee8e983c9144e78845..90a4eacfd7284314c663cba157baa72ec10ba9b9 100644 --- a/lib/widgets/application_card.dart +++ b/lib/widgets/application_card.dart @@ -12,9 +12,13 @@ class ApplicationCard extends StatefulWidget { static const route = AppUrl.homePage; final Application application; final bool isUpcomingApplication; + final bool isPastApplication; const ApplicationCard( - {Key? key, required this.application, this.isUpcomingApplication = false}) + {Key? key, + required this.application, + this.isUpcomingApplication = false, + this.isPastApplication = false}) : super(key: key); @override _ApplicationCardState createState() => _ApplicationCardState(); @@ -49,6 +53,7 @@ class _ApplicationCardState extends State<ApplicationCard> { MaterialPageRoute( builder: (context) => ApplicationDetailsPage( application: widget.application, + isPastApplication: widget.isPastApplication, ))); } }, diff --git a/lib/widgets/lead_inspector_application_field.dart b/lib/widgets/lead_inspector_application_field.dart index 686593987fea7b749001ca9eb79d2f0b8cd736f4..6a8b5ce10c2afaa95604ba5fed0ef1c34a6f482d 100644 --- a/lib/widgets/lead_inspector_application_field.dart +++ b/lib/widgets/lead_inspector_application_field.dart @@ -61,11 +61,11 @@ class _LeadInspectorApplicationFieldState _data = widget.fieldData[widget.fieldData.keys.elementAt(0)]; // print('Field: ' + _data.toString()); - _radioValue = _data[_data.keys.elementAt(0)]; - _summaryText = _data[_data.keys.elementAt(1)]; + _radioValue = _data[_data.keys.elementAt(0)] ?? ""; + _summaryText = _data[_data.keys.elementAt(1)] ?? ""; try { - _inspectionValue = _data[_data.keys.elementAt(2)]; - _attachment = _data[_data.keys.elementAt(3)]; + _inspectionValue = _data[_data.keys.elementAt(2)] ?? ""; + _attachment = _data[_data.keys.elementAt(3)] ?? ""; } catch (_) { return; } @@ -445,23 +445,23 @@ class _LeadInspectorApplicationFieldState InkWell( onTap: () { // print(_options[i]); - if (widget.applicationStatus != - InspectionStatus - .inspectionCompleted) { - setState(() { - _radioValue = _options[i]; - }); - if (_options[i] == - FieldValue.inCorrect) { - _displayCommentDialog(); - } - Map data = { - 'summaryText': _summaryText, - 'inspectionValue': - _inspectionValue - }; - triggerUpdate(data); + // if (widget.applicationStatus != + // InspectionStatus + // .inspectionCompleted) { + setState(() { + _radioValue = _options[i]; + }); + if (_options[i] == + FieldValue.inCorrect) { + _displayCommentDialog(); } + Map data = { + 'summaryText': _summaryText, + 'inspectionValue': + _inspectionValue + }; + triggerUpdate(data); + // } }, child: Container( padding: const EdgeInsets.only(