From 67da263e0d4e0e984d7d9ab76572b361e67ad955 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 15 Feb 2026 16:38:57 +0300 Subject: [PATCH 1/7] loan improvements --- lib/controllers/api_routes/urls.dart | 1 + lib/models/generic_attachment_model.dart | 7 +- .../loan_module/models/loan_form_model.dart | 4 +- .../loan_installation_pullout_form_model.dart | 60 ++++- .../models/loan_request_model.dart | 8 + .../pages/installation_form_view.dart | 223 ++++++++++++++++ .../pages/installation_pullout_form_view.dart | 242 ++++++++++-------- .../pages/intallation_details_view.dart | 106 ++++---- .../pages/loan_equipment_detail_page.dart | 184 +++++++------ .../pages/pullout_detail_page.dart | 29 +-- .../loan_module/provider/loan_provider.dart | 12 + 11 files changed, 616 insertions(+), 260 deletions(-) create mode 100644 lib/modules/loan_module/pages/installation_form_view.dart diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index f4bf299a..c66150e9 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -393,4 +393,5 @@ class URLs { static get getLoanByWorkOrderId => '$_baseUrl/Loan/GetLoanByWorkOrderId'; static get addLoan => '$_baseUrl/Loan/AddLoan'; + static get loanWorkflowAction => '$_baseUrl/Loan/LoanWorkflowAction'; } diff --git a/lib/models/generic_attachment_model.dart b/lib/models/generic_attachment_model.dart index 1d3cd5ea..821bcb59 100644 --- a/lib/models/generic_attachment_model.dart +++ b/lib/models/generic_attachment_model.dart @@ -1,12 +1,13 @@ import 'dart:io'; class GenericAttachmentModel { - GenericAttachmentModel({this.id, this.name,this.originalName,this.createdBy}); + GenericAttachmentModel({this.id, this.name, this.originalName, this.createdBy}); int? id; String? name; - String ?createdBy; + String? createdBy; String? originalName; + GenericAttachmentModel.fromJson(Map json) { print('created by here is ${json['createdBy']}'); id = json['id']; @@ -23,4 +24,4 @@ class GenericAttachmentModel { data['originalName'] = originalName; return data; } -} \ No newline at end of file +} diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index 575dd147..e1722ba7 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -30,7 +30,7 @@ class LoanFormModel { LoanFormModel({ this.docName, - this.isNewVendor=false, + this.isNewVendor = false, this.docNumber, this.docEmail, this.itemDescription, @@ -118,7 +118,9 @@ class LoanAttachments { num? loanId; String? attachmentName; String? attachmentDescription; + LoanAttachments({this.id, this.loanAttachmentTypeId, this.attachmentName, this.loanId, this.attachmentDescription}); + LoanAttachments.fromJson(dynamic json) { id = json['id']; loanAttachmentTypeId = json['loanAttachmentTypeId']; diff --git a/lib/modules/loan_module/models/loan_installation_pullout_form_model.dart b/lib/modules/loan_module/models/loan_installation_pullout_form_model.dart index bcbbff31..ff2129db 100644 --- a/lib/modules/loan_module/models/loan_installation_pullout_form_model.dart +++ b/lib/modules/loan_module/models/loan_installation_pullout_form_model.dart @@ -13,6 +13,9 @@ class LoanInstallationPullOutFormModel { DateTime? startTime; DateTime? endTime; double? totalHours; + int? loanId; + String? userId; + int? loanStatusId; LoanInstallationPullOutFormModel({ this.loanAttachment, @@ -23,18 +26,71 @@ class LoanInstallationPullOutFormModel { this.startTime, this.endTime, this.totalHours, + this.loanId, + this.loanStatusId, + this.userId, }); Map toJson() { //Need to check payload parm they need return { - 'snNo': snNo, - 'InstallationDate': date?.toIso8601String(), + // 'snNo': snNo, + 'serialNumber': snNo, + 'installationDate': date?.toIso8601String(), + 'pulloutDate': date?.toIso8601String(), "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], + "installationSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, "signature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + "pulloutSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, 'startTime': startTime?.toIso8601String(), 'endTime': endTime?.toIso8601String(), 'totalHours': totalHours, + 'loanId': loanId, + 'loanStatusId': loanStatusId, + 'userId': userId, + 'isFromMobile': true, + }; + } + + Map toInstallationJson() { + //Need to check payload parm they need + return { + // 'snNo': snNo, + 'serialNumber': snNo, + 'installationDate': date?.toIso8601String(), + // 'pulloutDate': date?.toIso8601String(), + "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], + "installationSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + // "signature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + // "pulloutSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + 'startTime': startTime?.toIso8601String(), + 'endTime': endTime?.toIso8601String(), + 'totalHours': totalHours, + 'loanId': loanId, + 'loanStatusId': loanStatusId, + 'userId': userId, + 'isFromMobile': true, + }; + } + + Map toPulloutJson() { + //Need to check payload parm they need + return { + // 'snNo': snNo, + 'serialNumber': snNo, + // 'installationDate': date?.toIso8601String(), + 'pulloutDate': date?.toIso8601String(), + "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], + // "installationSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + // "signature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + "pulloutSignature": signature != null ? "${DateTime.now().toIso8601String()}.png|${base64Encode(signature!)}" : null, + 'startTime': startTime?.toIso8601String(), + 'endTime': endTime?.toIso8601String(), + 'totalHours': totalHours, + 'loanId': loanId, + 'loanStatusId': loanStatusId, + 'userId': userId, + 'isFromMobile': true, }; } } diff --git a/lib/modules/loan_module/models/loan_request_model.dart b/lib/modules/loan_module/models/loan_request_model.dart index 2472f201..5757299f 100644 --- a/lib/modules/loan_module/models/loan_request_model.dart +++ b/lib/modules/loan_module/models/loan_request_model.dart @@ -55,6 +55,8 @@ class LoanRequestModel { int? pulloutWOItemId; int? id; String? installationDate; + String? startTime; + String? endTime; String? pulloutDate; String? createdBy; String? createdDate; @@ -114,6 +116,8 @@ class LoanRequestModel { this.id, this.createdBy, this.installationDate, + this.startTime, + this.endTime, this.pulloutDate, this.createdDate, this.modifiedBy, @@ -177,6 +181,8 @@ class LoanRequestModel { pulloutWOItemId = json['pulloutWOItemId']; id = json['id']; installationDate = json['installationDate']; + startTime = json['startTime']; + endTime = json['endTime']; pulloutDate = json['pulloutDate']; createdBy = json['createdBy']; createdDate = json['createdDate']; @@ -239,6 +245,8 @@ class LoanRequestModel { data['pulloutWOItemId'] = this.pulloutWOItemId; data['id'] = this.id; data['installationDate'] = this.installationDate; + data['startTime'] = this.startTime; + data['endTime'] = this.endTime; data['pulloutDate'] = this.pulloutDate; data['createdBy'] = this.createdBy; data['createdDate'] = this.createdDate; diff --git a/lib/modules/loan_module/pages/installation_form_view.dart b/lib/modules/loan_module/pages/installation_form_view.dart new file mode 100644 index 00000000..f39b3ac6 --- /dev/null +++ b/lib/modules/loan_module/pages/installation_form_view.dart @@ -0,0 +1,223 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/models/timer_model.dart'; +import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; +import 'package:test_sa/modules/loan_module/models/loan_installation_pullout_form_model.dart'; +import 'package:test_sa/modules/loan_module/models/loan_request_model.dart'; +import 'package:test_sa/modules/loan_module/provider/loan_provider.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/default_app_bar.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; +import 'package:test_sa/views/widgets/timer/app_timer.dart'; + +class InstallationFormView extends StatefulWidget { + LoanRequestModel? loanData; + bool isInstallation; + + InstallationFormView({Key? key, this.loanData, this.isInstallation = true}) : super(key: key); + + @override + _InstallationFormViewState createState() { + return _InstallationFormViewState(); + } +} + +class _InstallationFormViewState extends State { + final _formKey = GlobalKey(); + List _attachments = []; + + final LoanInstallationPullOutFormModel _formData = LoanInstallationPullOutFormModel(); + + @override + void initState() { + super.initState(); + _formData.loanId = widget.loanData?.id; + } + + @override + void dispose() { + super.dispose(); + } + + void updateTimer({TimerModel? timer}) { + if (timer?.startAt != null && timer?.endAt != null) { + final start = timer!.startAt!; + final end = timer.endAt!; + final difference = end.difference(start); + final totalHours = difference.inSeconds / 3600.0; + _formData.startTime = start; + _formData.endTime = end; + _formData.totalHours = totalHours; + } + + if (timer == null) { + _formData.startTime = null; + _formData.endTime = null; + _formData.totalHours = null; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: DefaultAppBar(title: 'Installation Report'.addTranslation), + body: Form( + key: _formKey, + child: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (widget.isInstallation) ...[ + AppTextFormField( + initialValue: "", + labelText: "Updates SN", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + textInputAction: TextInputAction.next, + onChange: (value) { + _formData.snNo = value; + }, + ), + 8.height, + ], + ADatePicker( + label: widget.isInstallation ? "Installation Date".addTranslation : "Validation Date".addTranslation, + hideShadow: true, + backgroundColor: AppColor.fieldBgColor(context), + to: DateTime.now().add(const Duration(days: 365)), + formatDateWithTime: true, + date: _formData.date, + onDatePicker: (DateTime date) async { + final time = await showTimePicker(context: context, initialTime: TimeOfDay.now()); + if (time == null) return; + setState(() { + _formData.date = DateTime(date.year, date.month, date.day, time.hour, time.minute); + }); + }, + ), + 8.height, + AppTimer( + label: context.translation.workingHours, + timer: _formData.timerModel, + // pickerFromDate: DateTime.tryParse(widget.createdDate ?? ''), + pickerFromDate: DateTime(DateTime.now().year, DateTime.now().month, 1), + pickerTimer: _formData.timerModel, + showTimer: false, + onPick: (timer) { + updateTimer(timer: timer); + }, + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(10), + ), + timerProgress: (isRunning) {}, + onChange: (timer) async { + updateTimer(timer: timer); + return true; + }, + ), + ESignature( + title: 'End-user signature'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + oldSignature: '', + newSignature: _formData.signature, + onChange: (Uint8List signature) { + if (signature.isEmpty) return; + setState(() { + _formData.signature = signature; + }); + }, + ), + 24.height, + "Attachments".bodyText(context).custom(color: AppColor.black10), + 8.height, + AttachmentPicker( + label: context.translation.attachments, + attachment: _attachments, + buttonColor: AppColor.primary10, + onlyImages: false, + showAsListView: true, + buttonIcon: 'attachment_icon'.toSvgAsset(color: AppColor.primary10), + onChange: (attachments) { + _attachments = attachments; + setState(() {}); + }, + ), + ], + ).toShadowContainer(context, borderRadius: 20), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: context.translation.submitRequest, + onPressed: _submitForm, + ), + ), + ], + ), + ), + ); + } + + Future _submitForm() async { + if (!_formKey.currentState!.validate()) return; + if (_formData.date == null) { + "Please select installation date".showToast; + return; + } + if (_formData.startTime == null || _formData.endTime == null) { + "Please select start and end time".showToast; + return; + } + if (_formData.signature == null || _formData.signature!.isEmpty) { + "Signature is required".showToast; + return; + } + + _formData.loanAttachment = []; + + for (var item in _attachments) { + String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; + _formData.loanAttachment!.add(LoanAttachments(id: 0, attachmentName: fileName, loanId: widget.loanData?.id)); + } + Utils.showLoading(context); + _formData.loanStatusId = widget.loanData?.loanStatusValue; + _formData.userId = context.userProvider.user?.userID; + LoanProvider incidentProvider = Provider.of(context, listen: false); + bool isSuccess = await incidentProvider.loanWorkflowAction(_formData.toInstallationJson()); + Utils.hideLoading(context); + if (isSuccess) { + Navigator.pop(context, true); + } + } +} diff --git a/lib/modules/loan_module/pages/installation_pullout_form_view.dart b/lib/modules/loan_module/pages/installation_pullout_form_view.dart index 11a3cbfd..ca70faa3 100644 --- a/lib/modules/loan_module/pages/installation_pullout_form_view.dart +++ b/lib/modules/loan_module/pages/installation_pullout_form_view.dart @@ -1,16 +1,24 @@ +import 'dart:convert'; import 'dart:developer'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/timer_model.dart'; +import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; import 'package:test_sa/modules/loan_module/models/loan_installation_pullout_form_model.dart'; +import 'package:test_sa/modules/loan_module/models/loan_request_model.dart'; +import 'package:test_sa/modules/loan_module/provider/loan_provider.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'; @@ -21,10 +29,10 @@ import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; class InstallationPullOutFormView extends StatefulWidget { - final bool isPullout; final String? createdDate; + final LoanRequestModel? loanData; - const InstallationPullOutFormView({super.key, required this.isPullout, this.createdDate}); + const InstallationPullOutFormView({super.key, this.createdDate, this.loanData}); @override State createState() => _InstallationPullOutFormViewState(); @@ -34,7 +42,16 @@ class _InstallationPullOutFormViewState extends State(); final _snController = TextEditingController(); List _attachments = []; - final _formData = LoanInstallationPullOutFormModel(); + final LoanInstallationPullOutFormModel _formData = LoanInstallationPullOutFormModel(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + _formData.loanId = widget.loanData?.id; + _formData.loanStatusId = widget.loanData?.loanStatusValue; + _formData.userId = context.userProvider.user?.userID; + } @override void dispose() { @@ -66,23 +83,6 @@ class _InstallationPullOutFormViewState extends State (v == null || v.isEmpty) ? 'Required' : null, - ), - 8.height, - ], - ADatePicker( - label: !widget.isPullout ? "Installation Date".addTranslation : "Validation Date".addTranslation, - hideShadow: true, - backgroundColor: AppColor.fieldBgColor(context), - to: DateTime.now().add(const Duration(days: 365)), - formatDateWithTime: true, - onDatePicker: _pickInstallationDate, - ), - 8.height, - AppTimer( - label: context.translation.workingHours, - timer: _formData.timerModel, - // pickerFromDate: DateTime.tryParse(widget.createdDate ?? ''), - pickerFromDate: DateTime(DateTime.now().year, DateTime.now().month, 1), - pickerTimer: _formData.timerModel, - showTimer: false, - onPick: (timer) { - updateTimer(timer: timer); - }, - width: double.infinity, - decoration: BoxDecoration( - color: AppColor.fieldBgColor(context), - borderRadius: BorderRadius.circular(10), - ), - timerProgress: (isRunning) {}, - onChange: (timer) async { - updateTimer(timer: timer); - return true; - }, - ), - ESignature( - title: 'End-user signature'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), - showShadow: false, - oldSignature: '', - newSignature: _formData.signature, - onChange: _onSignatureChanged, - ), - 24.height, - AttachmentPicker( - label: context.translation.attachments, - attachment: _attachments, + // appBar: DefaultAppBar(title: 'Pullout Report'.addTranslation), + body: Form( + key: _formKey, + child: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ADatePicker( + label: "Validation Date".addTranslation, + hideShadow: true, + backgroundColor: AppColor.fieldBgColor(context), + to: DateTime.now().add(const Duration(days: 365)), + formatDateWithTime: true, + date: _formData.date, + onDatePicker: (DateTime date) async { + final time = await showTimePicker(context: context, initialTime: TimeOfDay.now()); + if (time == null) return; + setState(() { + _formData.date = DateTime(date.year, date.month, date.day, time.hour, time.minute); + }); + }, + ), + // ADatePicker( + // label: !widget.isPullout ? "Installation Date".addTranslation : "Validation Date".addTranslation, + // hideShadow: true, + // backgroundColor: AppColor.fieldBgColor(context), + // to: DateTime.now().add(const Duration(days: 365)), + // formatDateWithTime: true, + // onDatePicker: _pickInstallationDate, + // ), + 8.height, + AppTimer( + label: context.translation.workingHours, + timer: _formData.timerModel, + // pickerFromDate: DateTime.tryParse(widget.createdDate ?? ''), + pickerFromDate: DateTime(DateTime.now().year, DateTime.now().month, 1), + pickerTimer: _formData.timerModel, + showTimer: false, + onPick: (timer) { + updateTimer(timer: timer); + }, + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(10), ), - ], - ).toShadowContainer(context, borderRadius: 20), - ).expanded, - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton( - buttonColor: AppColor.primary10, - label: context.translation.submitRequest, - onPressed: _submitForm, - ), + timerProgress: (isRunning) {}, + onChange: (timer) async { + updateTimer(timer: timer); + return true; + }, + ), + ESignature( + title: 'End-user signature'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + oldSignature: '', + newSignature: _formData.signature, + onChange: _onSignatureChanged, + ), + 24.height, + "Attachments".bodyText(context).custom(color: AppColor.black10), + 8.height, + AttachmentPicker( + label: context.translation.attachments, + attachment: _attachments, + buttonColor: AppColor.primary10, + onlyImages: false, + showAsListView: true, + buttonIcon: 'attachment_icon'.toSvgAsset(color: AppColor.primary10), + onChange: (attachments) { + _attachments = attachments; + setState(() {}); + }, + ), + // AttachmentPicker( + // label: context.translation.attachments, + // attachment: _attachments, + // ), + ], + ).toShadowContainer(context, borderRadius: 20), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: context.translation.submitRequest, + onPressed: _submitForm, ), - ], - ), + ), + ], ), ), ); } + + Future _submitForm() async { + if (!_formKey.currentState!.validate()) return; + if (_formData.date == null) { + "Please select Validation date".showToast; + return; + } + if (_formData.startTime == null || _formData.endTime == null) { + "Please select start and end time".showToast; + return; + } + if (_formData.signature == null || _formData.signature!.isEmpty) { + "Signature is required".showToast; + return; + } + + _formData.loanAttachment = []; + + for (var item in _attachments) { + String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; + _formData.loanAttachment!.add(LoanAttachments(id: 0, attachmentName: fileName, loanId: widget.loanData?.id)); + } + Utils.showLoading(context); + LoanProvider incidentProvider = Provider.of(context, listen: false); + bool isSuccess = await incidentProvider.loanWorkflowAction(_formData.toPulloutJson()); + Utils.hideLoading(context); + if (isSuccess) { + Navigator.pop(context, true); + } + } } diff --git a/lib/modules/loan_module/pages/intallation_details_view.dart b/lib/modules/loan_module/pages/intallation_details_view.dart index 67541c93..8bfd6e8b 100644 --- a/lib/modules/loan_module/pages/intallation_details_view.dart +++ b/lib/modules/loan_module/pages/intallation_details_view.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/loan_module/models/loan_request_model.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; @@ -16,72 +18,76 @@ class InstallationDetailsView extends StatelessWidget { @override Widget build(BuildContext context) { + String workingTime = "-"; + + if (loanData.startTime != null && loanData.endTime != null) { + try { + final timeFormat = DateFormat('h:mm a'); // Matches "3:00 PM" format + final now = DateTime.now(); + + final start = timeFormat.parse(loanData.startTime!); + var end = timeFormat.parse(loanData.endTime!); + + // Set the date to today for both times + var startDateTime = DateTime(now.year, now.month, now.day, start.hour, start.minute); + var endDateTime = DateTime(now.year, now.month, now.day, end.hour, end.minute); + + // If end time is before start time, shift crossed midnight + if (endDateTime.isBefore(startDateTime)) { + endDateTime = endDateTime.add(const Duration(days: 1)); + } + + double totalWorkingTime = endDateTime.difference(startDateTime).inSeconds / 3600.0; + } catch (ex) {} + } + + // if (loanData.startTime != null && loanData.endTime != null) { + // final start = DateTime.parse(loanData.startTime!); + // var end = DateTime.parse(loanData.endTime!); + // + // // If end time is before start time, it means the shift crossed midnight + // if (end.isBefore(start)) { + // end = end.add(const Duration(days: 1)); + // } + // + // double totalWorkingTime = end.difference(start).inSeconds / 3600.0; + // workingTime = CMRequestUtils.formatTotalWorkingHours(totalWorkingTime); + // } + + // if (loanData.startTime != null && loanData.endTime != null) { + // final start = DateTime.parse(loanData.startTime!); + // final end = DateTime.parse(loanData.endTime!); + // double totalWorkingTime = end.difference(start).inSeconds / 3600.0; + // workingTime = CMRequestUtils.formatTotalWorkingHours(totalWorkingTime); + // } + return Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - detailContent( - context: context, - heading: 'Updates SN:', - label: loanData.installationDate??'-' - ), - detailContent( - context: context, - heading: 'Installation Date', - label: loanData.installationDate??'-' - ), - ], - ), - 12.height, - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - detailContent( - context: context, - heading: 'Start Time', - label: loanData.installationDate??'-' - ), - detailContent( - context: context, - heading: 'End Time', - label: loanData.installationDate??'-' - ), - ], - ), - 12.height, - detailContent( - context: context, - heading: 'Total Time', - label: loanData.installationDate??'-' + Text( + "Installation Details".addTranslation, + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), + 4.height, + 'Updates SN: ${loanData.assetSerialNumber ?? "-"}'.bodyText(context), + 'Installation Date: ${loanData.installationDate?.toAssetDetailsFormat ?? "-"}'.bodyText(context), + 'Start Time: ${loanData.startTime ?? "-"}'.bodyText(context), + 'End Time: ${loanData.endTime ?? "-"}'.bodyText(context), + 'Total Working Time: $workingTime'.bodyText(context), if (loanData.loanAttachments!.isNotEmpty) ...[ 12.height, Text( "Uploads Phase 2 docs".addTranslation, - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), 8.height, - FilesList(images: loanData.loanAttachments?.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), + FilesList(showAsListView: true, images: loanData.loanAttachments?.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), ], ], ).toShadowContainer(context, borderRadius: 20), ); } - - Widget detailContent({required BuildContext context, required String label, required String heading}) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - heading.bodyText(context).custom(color: AppColor.neutral120), - 6.height, - //TODO Need to check which value need to show here - label.bodyText(context).custom(color: AppColor.neutral120), - ], - ); - } } diff --git a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart index 8506292c..2b1175aa 100644 --- a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart +++ b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart @@ -8,6 +8,7 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/loan_module/models/loan_attachment_model.dart'; +import 'package:test_sa/modules/loan_module/pages/installation_form_view.dart'; import 'package:test_sa/modules/loan_module/pages/pullout_detail_page.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -20,101 +21,118 @@ import 'package:test_sa/views/widgets/requests/request_status.dart'; import '../models/loan_request_model.dart'; import 'installation_pullout_form_view.dart'; -class LoanEquipmentDetailPage extends StatelessWidget { +class LoanEquipmentDetailPage extends StatefulWidget { static const String id = "/loan-equipment-detail-page"; final int loanId; LoanEquipmentDetailPage({Key? key, required this.loanId}) : super(key: key); + @override + _LoanEquipmentDetailPageState createState() { + return _LoanEquipmentDetailPageState(); + } +} + +class _LoanEquipmentDetailPageState extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: const DefaultAppBar(title: "Request Details"), - body: SafeArea( - child: FutureBuilder( - future: Provider.of(context, listen: false).getLoanById(loanId), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center; - if (snapshot.data == null) return const NoDataFound().center; - - List allAttachments = snapshot.data!.loanAttachments!; - - return Column( - children: [ - ListView( - padding: const EdgeInsets.all(16), - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - StatusLabel( - label: snapshot.data!.loanStatusName!, - textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!), - backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!), - ), - 1.width.expanded, - Text( - snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-", - textAlign: TextAlign.end, - style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), - ), - ], - ), - 12.height, - ...requesterDetails(context, snapshot.data!), - 12.height, - ...requestDetails(context, snapshot.data!), - 12.height, - ...assetDetails(context, snapshot.data!), - 12.height, - ...installationDetails(context, snapshot.data!), - 12.height, - ...doctorDetails(context, snapshot.data!), - 12.height, - ...vendorDetails(context, snapshot.data!), - if (allAttachments.isNotEmpty) ...[ - const Divider().defaultStyle(context), + body: FutureBuilder( + future: Provider.of(context, listen: false).getLoanById(widget.loanId), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center; + if (snapshot.data == null) return const NoDataFound().center; + + List allAttachments = snapshot.data!.loanAttachments!; + + return Column( + children: [ + ListView( + padding: const EdgeInsets.all(16), + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: snapshot.data!.loanStatusName!, + textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!), + backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!), + ), + 1.width.expanded, Text( - "Attachments".addTranslation, - style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), ), - FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), ], + ), + 12.height, + ...requesterDetails(context, snapshot.data!), + const Divider().defaultStyle(context), + ...requestDetails(context, snapshot.data!), + const Divider().defaultStyle(context), + ...assetDetails(context, snapshot.data!), + const Divider().defaultStyle(context), + ...installationDetails(context, snapshot.data!), + const Divider().defaultStyle(context), + ...doctorDetails(context, snapshot.data!), + const Divider().defaultStyle(context), + ...vendorDetails(context, snapshot.data!), + if (allAttachments.isNotEmpty) ...[ + const Divider().defaultStyle(context), + Text( + "Attachments".addTranslation, + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), ], - ).toShadowContainer(context, padding: 12), - ], - ).expanded, - // if (context.userProvider.isEngineer && (snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) - // FooterActionButton.footerContainer( - // context: context, - // child: AppFilledButton( - // onPressed: () async { - // if (snapshot.data?.loanStatusValue == 5) { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => PullOutDetailsPage( - // loanData: snapshot.data, - // ))); - // return; - // } - // Navigator.push(context, MaterialPageRoute(builder: (context) => const InstallationPullOutFormView(isPullout: false))); - // }, - // label: snapshot.data?.loanStatusValue == 5 ? 'Installation Report' : 'PullOut Report', - // ), - // ) - ], - ); - }), - )); + ], + ).toShadowContainer(context, padding: 12), + ], + ).expanded, + // @todo ask backend to add loanTypeValue, so we can show or hide the buttons + if (snapshot.data?.loanTypeName == "Standard" && context.userProvider.isEngineer && (snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + onPressed: () async { + if (snapshot.data?.loanStatusValue == 5) { + bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => InstallationFormView(loanData: snapshot.data))) ?? false; + if (isSuccess) { + setState(() {}); + } + return; + } + bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => PullOutDetailsPage(loanData: snapshot.data))) ?? false; + if (isSuccess) { + setState(() {}); + } + }, + label: snapshot.data?.loanStatusValue == 5 ? 'Installation Report' : 'PullOut Report', + ), + ) + ], + ); + })); } List requestDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Request Details", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), 'Loan Type: ${loanData.loanTypeName ?? "-"}'.bodyText(context), 'Loan No: ${loanData.reqCode ?? "-"}'.bodyText(context), 'Loan Period: ${loanData.loanPeriodName ?? "-"}'.bodyText(context), @@ -127,7 +145,7 @@ class LoanEquipmentDetailPage extends StatelessWidget { List vendorDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Vendor Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Vendor Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), 'Name: ${loanData.vendorName ?? "-"}'.bodyText(context), 'Representative Name: ${loanData.vendorRepName ?? "-"}'.bodyText(context), 'Contact: ${loanData.vendorContact ?? "-"}'.bodyText(context), @@ -137,7 +155,7 @@ class LoanEquipmentDetailPage extends StatelessWidget { List assetDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Asset Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Asset Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), '${context.translation.assetName}: ${loanData.assetName ?? "-"}'.bodyText(context), '${context.translation.assetNo}: ${loanData.assetNumber ?? "-"}'.bodyText(context), '${context.translation.serialNumber}: ${loanData.assetSerialNumber ?? "-"}'.bodyText(context), @@ -148,7 +166,7 @@ class LoanEquipmentDetailPage extends StatelessWidget { List installationDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Installation & Pullout Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Installation & Pullout Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), 'Installation EDD: ${loanData.installationEDD?.toServiceRequestDetailsFormat ?? "-"}'.bodyText(context), 'Installation Date: ${loanData.installationEDD?.toAssetDetailsFormat ?? "-"}'.bodyText(context), 'Pullout EDD: ${loanData.pulloutEDD?.toServiceRequestDetailsFormat ?? "-"}'.bodyText(context), @@ -158,7 +176,7 @@ class LoanEquipmentDetailPage extends StatelessWidget { List doctorDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Doctor Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Doctor Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), 'Name: ${loanData.doctorName ?? "-"}'.bodyText(context), 'Contact: ${loanData.doctorContact ?? "-"}'.bodyText(context), 'Email: ${loanData.doctorEmail ?? "-"}'.bodyText(context), @@ -167,7 +185,7 @@ class LoanEquipmentDetailPage extends StatelessWidget { List requesterDetails(BuildContext context, LoanRequestModel loanData) { return [ - Text("Requester Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + Text("Requester Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), 'Name: ${loanData.requestorUserIdForDisplay ?? "-"}'.bodyText(context), 'Email: ${loanData.requestorUserEmail ?? "-"}'.bodyText(context), 'Contact: ${loanData.requestorUserMobileNumber ?? "-"}'.bodyText(context), diff --git a/lib/modules/loan_module/pages/pullout_detail_page.dart b/lib/modules/loan_module/pages/pullout_detail_page.dart index 09b81048..9992ba8c 100644 --- a/lib/modules/loan_module/pages/pullout_detail_page.dart +++ b/lib/modules/loan_module/pages/pullout_detail_page.dart @@ -10,29 +10,17 @@ import 'package:test_sa/modules/loan_module/pages/intallation_details_view.dart' import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; -class PullOutDetailsPage extends StatefulWidget { +class PullOutDetailsPage extends StatelessWidget { final LoanRequestModel? loanData; PullOutDetailsPage({Key? key, required this.loanData}) : super(key: key); - @override - _PullOutDetailsPageState createState() { - return _PullOutDetailsPageState(); - } -} - -class _PullOutDetailsPageState extends State { - @override - void dispose() { - super.dispose(); - } - @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: DefaultAppBar( - title: 'Loan Request'.addTranslation, + title: 'Pullout Report'.addTranslation, ), body: DefaultTabController( length: 2, @@ -61,16 +49,15 @@ class _PullOutDetailsPageState extends State { ], ), ), - 12.height, + 0.height, TabBarView( children: [ - Align( - alignment: Alignment.topCenter, - child: InstallationDetailsView( - loanData: widget.loanData!, - ), + Column( + children: [ + InstallationDetailsView(loanData: loanData!), + ], ), - const InstallationPullOutFormView(isPullout: true), + InstallationPullOutFormView(loanData: loanData!), ], ).expanded, ], diff --git a/lib/modules/loan_module/provider/loan_provider.dart b/lib/modules/loan_module/provider/loan_provider.dart index 01153eef..d3ea225b 100644 --- a/lib/modules/loan_module/provider/loan_provider.dart +++ b/lib/modules/loan_module/provider/loan_provider.dart @@ -48,4 +48,16 @@ class LoanProvider extends ChangeNotifier { } return loanData; } + + Future loanWorkflowAction(Map body) async { + try { + Response response = await ApiManager.instance.post(URLs.loanWorkflowAction, body: body, showToast: true); + if (response.statusCode >= 200 && response.statusCode < 300) { + return true; + } + return false; + } catch (error) { + return false; + } + } } From 66b8ea59d94a29734f54fd569de8140fc7cdf327 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 16 Feb 2026 12:40:27 +0300 Subject: [PATCH 2/7] signature added. --- lib/models/new_models/task_request/task_request_model.dart | 4 ++++ lib/modules/tm_module/tasks/update_task_request_view.dart | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/models/new_models/task_request/task_request_model.dart b/lib/models/new_models/task_request/task_request_model.dart index 6d7c9956..235b3851 100644 --- a/lib/models/new_models/task_request/task_request_model.dart +++ b/lib/models/new_models/task_request/task_request_model.dart @@ -491,6 +491,7 @@ class TaskTypeModel { final List? taskTypeRoles; final bool? isInstallation; final bool? isRecallAndAlert; + final bool? isSignatureRequired; TaskTypeModel({ this.id, @@ -500,6 +501,7 @@ class TaskTypeModel { this.taskTypeRoles, this.isInstallation, this.isRecallAndAlert, + this.isSignatureRequired, }); factory TaskTypeModel.fromJson(Map json) => TaskTypeModel( @@ -510,6 +512,7 @@ class TaskTypeModel { taskTypeRoles: json['taskTypeRoles'], isInstallation: json['isInstallation'], isRecallAndAlert: json['isRecallAndAlert'], + isSignatureRequired: json['isSignatureRequired'], ); Map toJson() => { @@ -520,6 +523,7 @@ class TaskTypeModel { 'taskTypeRoles': taskTypeRoles, 'isInstallation': isInstallation, 'isRecallAndAlert': isRecallAndAlert, + 'isSignatureRequired': isSignatureRequired, }; } diff --git a/lib/modules/tm_module/tasks/update_task_request_view.dart b/lib/modules/tm_module/tasks/update_task_request_view.dart index 72fd8ab3..f1a44630 100644 --- a/lib/modules/tm_module/tasks/update_task_request_view.dart +++ b/lib/modules/tm_module/tasks/update_task_request_view.dart @@ -156,7 +156,7 @@ class _UpdateTaskRequestState extends State { recallAlertTypeWidget(taskModel: taskProvider.taskRequestModel!), ], //Not good approach need to use enums .... - if (taskProvider.taskRequestModel?.taskType?.typeName == "PullOut") ...[ + if (taskProvider.taskRequestModel?.taskType?.isSignatureRequired ?? false) ...[ ESignature( title: "End-user signature", backgroundColor: AppColor.fieldBgColor(context), From 6c6ba32997ab60cc197ad5fbeb169d3388889b24 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 18 Feb 2026 11:32:28 +0300 Subject: [PATCH 3/7] delivered status added. --- lib/extensions/context_extension.dart | 6 +- lib/extensions/text_extensions.dart | 2 +- lib/extensions/widget_extensions.dart | 18 +++++- .../pages/installation_pullout_form_view.dart | 4 +- .../pages/loan_equipment_detail_page.dart | 58 ++++++++++++++++++- 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/lib/extensions/context_extension.dart b/lib/extensions/context_extension.dart index f85af74c..5e304680 100644 --- a/lib/extensions/context_extension.dart +++ b/lib/extensions/context_extension.dart @@ -14,6 +14,8 @@ extension BuildContextExtension on BuildContext { bool get isDark => Provider.of(this).theme == "dark"; + bool get isDarkNotListen => Provider.of(this, listen: false).theme == "dark"; + bool get isAr => Provider.of(this).language == "ar"; SettingProvider get settingProvider => Provider.of(this, listen: false); @@ -36,12 +38,12 @@ extension BuildContextExtension on BuildContext { builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title,okTitle: okTitle), ); - Future showBottomSheet(Widget childWidget, {bool? isDismissible, String? title}) => showModalBottomSheet( + Future showBottomSheet(Widget childWidget, {bool? isDismissible, String? title, bool showCancelButton = false}) => showModalBottomSheet( context: this, useSafeArea: true, isScrollControlled: true, isDismissible: true, backgroundColor: Colors.transparent, - builder: (context) => SingleChildScrollView(padding: const EdgeInsets.all(0), child: childWidget.bottomSafeArea).bottomSheetContainerNew(context, title: title), + builder: (context) => SingleChildScrollView(padding: const EdgeInsets.all(0), child: childWidget.bottomSafeArea).bottomSheetContainerNew(context, title: title,showCancelButton: showCancelButton), ); } diff --git a/lib/extensions/text_extensions.dart b/lib/extensions/text_extensions.dart index e608131f..5ec00e5b 100644 --- a/lib/extensions/text_extensions.dart +++ b/lib/extensions/text_extensions.dart @@ -23,7 +23,7 @@ extension TextStyles on String { Text heading6(BuildContext context) => getTextWithStyle(this, AppTextStyles.heading6, context.isDark ? AppColor.neutral30 : AppColor.neutral50); - Text bodyText(BuildContext context) => getTextWithStyle(this, AppTextStyles.bodyText, context.isDark ? AppColor.neutral10 : AppColor.neutral20); + Text bodyText(BuildContext context) => getTextWithStyle(this, AppTextStyles.bodyText, context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral20); Text bodyText2(BuildContext context) => getTextWithStyle(this, AppTextStyles.bodyText2, context.isDark ? AppColor.neutral10 : AppColor.neutral20); diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index 25aee136..00fc6aa7 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -145,7 +145,7 @@ extension WidgetExtensions on Widget { child: this, ); - Widget bottomSheetContainerNew(BuildContext context, {EdgeInsets? padding, String? title}) => Container( + Widget bottomSheetContainerNew(BuildContext context, {EdgeInsets? padding, String? title, bool showCancelButton = false}) => Container( clipBehavior: Clip.antiAlias, margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), decoration: BoxDecoration( @@ -164,7 +164,21 @@ extension WidgetExtensions on Widget { ).center, 16.height, if (title != null) ...[ - title.bottomSheetHeadingTextStyle(context), + Row( + children: [ + title.bottomSheetHeadingTextStyle(context).expanded, + if (showCancelButton) ...[ + 8.width, + InkWell( + onTap: () => Navigator.pop(context), + child: Icon( + Icons.cancel_outlined, + color: context.isDarkNotListen ? AppColor.neutral30 : AppColor.black20, + ), + ) + ] + ], + ), 16.height, ], this diff --git a/lib/modules/loan_module/pages/installation_pullout_form_view.dart b/lib/modules/loan_module/pages/installation_pullout_form_view.dart index ca70faa3..6d60fb2d 100644 --- a/lib/modules/loan_module/pages/installation_pullout_form_view.dart +++ b/lib/modules/loan_module/pages/installation_pullout_form_view.dart @@ -219,8 +219,8 @@ class _InstallationPullOutFormViewState extends State(context, listen: false); - bool isSuccess = await incidentProvider.loanWorkflowAction(_formData.toPulloutJson()); + LoanProvider loanProvider = Provider.of(context, listen: false); + bool isSuccess = await loanProvider.loanWorkflowAction(_formData.toPulloutJson()); Utils.hideLoading(context); if (isSuccess) { Navigator.pop(context, true); diff --git a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart index 2b1175aa..a802e90b 100644 --- a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart +++ b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart @@ -6,6 +6,7 @@ import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/loan_module/models/loan_attachment_model.dart'; import 'package:test_sa/modules/loan_module/pages/installation_form_view.dart'; @@ -13,6 +14,7 @@ import 'package:test_sa/modules/loan_module/pages/pullout_detail_page.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.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/default_app_bar.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; @@ -105,11 +107,59 @@ class _LoanEquipmentDetailPageState extends State { ], ).expanded, // @todo ask backend to add loanTypeValue, so we can show or hide the buttons - if (snapshot.data?.loanTypeName == "Standard" && context.userProvider.isEngineer && (snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) + if (snapshot.data?.loanTypeName == "Standard" && + context.userProvider.isEngineer && + (snapshot.data?.loanStatusValue == 4 || snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) FooterActionButton.footerContainer( context: context, child: AppFilledButton( onPressed: () async { + if (snapshot.data?.loanStatusValue == 4) { + String comment = ""; + context.showBottomSheet( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + 'Estimated Date: ${snapshot.data?.installationEDD?.toAssetDetailsFormat ?? "-"}'.bodyText(context), + 16.height, + AppTextFormField( + labelText: context.translation.comments, + showSpeechToText: true, + textInputType: TextInputType.multiline, + labelStyle: AppTextStyles.textFieldLabelStyle, + showWithoutDecoration: true, + backgroundColor: context.isDarkNotListen ? AppColor.neutral20 : AppColor.neutral100, + alignLabelWithHint: true, + onChange: (text) { + comment = text; + }, + ), + 16.height, + AppFilledButton( + onPressed: () async { + Utils.showLoading(context); + LoanProvider loanProvider = Provider.of(context, listen: false); + bool isSuccess = await loanProvider.loanWorkflowAction({ + "userId": context.userProvider.user!.userID, + "isFromMobile": true, + "commentsFromMobile": comment, + 'loanId': snapshot.data?.id, + 'loanStatusId': snapshot.data?.loanStatusValue, + }); + Utils.hideLoading(context); + if (isSuccess) { + Navigator.pop(context, true); + setState(() {}); + } + }, + label: "Delivered"), + ], + ), + title: "EDD & Comments", + showCancelButton: true); + return; + } if (snapshot.data?.loanStatusValue == 5) { bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => InstallationFormView(loanData: snapshot.data))) ?? false; if (isSuccess) { @@ -122,7 +172,11 @@ class _LoanEquipmentDetailPageState extends State { setState(() {}); } }, - label: snapshot.data?.loanStatusValue == 5 ? 'Installation Report' : 'PullOut Report', + label: snapshot.data?.loanStatusValue == 4 + ? "Delivered" + : snapshot.data?.loanStatusValue == 5 + ? 'Installation Report' + : 'PullOut Report', ), ) ], From 5dee736d2890b8d57bb1ff034a2b69d047e0a1dc Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 18 Feb 2026 13:35:43 +0300 Subject: [PATCH 4/7] multiple timer entries bux fixed. --- lib/modules/tm_module/tasks/update_task_request_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/tm_module/tasks/update_task_request_view.dart b/lib/modules/tm_module/tasks/update_task_request_view.dart index f1a44630..141ac7c0 100644 --- a/lib/modules/tm_module/tasks/update_task_request_view.dart +++ b/lib/modules/tm_module/tasks/update_task_request_view.dart @@ -262,7 +262,7 @@ class _UpdateTaskRequestState extends State { taskAttachment.add(TaskJobAttachment(id: item.id, name: fileName)); } taskModel.taskJobAttachments = taskAttachment; - + taskModel.taskJobActivityEngineerTimers = []; if (taskModel.taskTimePicker != null) { int durationInSecond = taskModel.taskTimePicker!.endAt!.difference(taskModel.taskTimePicker!.startAt!).inSeconds; taskModel.taskJobActivityEngineerTimers?.add( From 4fdd04b1066acf7f69f5907d23abb60133d59b17 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 18 Feb 2026 14:40:51 +0300 Subject: [PATCH 5/7] unread messages api added. --- lib/controllers/api_routes/urls.dart | 1 + .../widgets/app_bar_widget.dart | 8 + lib/modules/cx_module/chat/chat_provider.dart | 15 +- .../user/notifications/unread_chat_list.dart | 153 ++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 lib/views/pages/user/notifications/unread_chat_list.dart diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index c66150e9..fb160d9c 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -18,6 +18,7 @@ class URLs { static const String chatHubUrlApi = "$chatHubUrl/api"; // new V2 apis static const String chatHubUrlChat = "$chatHubUrl/hubs/chat"; // new V2 apis static const String resetMessageCount = "$chatHubUrlApi/userChatHistory/AckMessages"; // new V2 apis + static const String unreadMessages = "$chatHubUrlApi/userChatHistory/unread-messages"; // new V2 apis static const String chatApiKey = "f53a98286f82798d588f67a7f0db19f7aebc839e"; // new V2 apis static String _host = host1; diff --git a/lib/dashboard_latest/widgets/app_bar_widget.dart b/lib/dashboard_latest/widgets/app_bar_widget.dart index 5134875a..44a15be6 100644 --- a/lib/dashboard_latest/widgets/app_bar_widget.dart +++ b/lib/dashboard_latest/widgets/app_bar_widget.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; @@ -10,6 +11,7 @@ import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/pages/user/notifications/notifications_page.dart'; +import 'package:test_sa/views/pages/user/notifications/unread_chat_list.dart'; import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; import '../../controllers/providers/api/user_provider.dart'; @@ -127,6 +129,12 @@ class AppBarWidget extends StatelessWidget { ); }), 16.width, + if (context.settingProvider.isUserFlowMedical) ...[ + Icon(Icons.mark_unread_chat_alt_outlined, color: context.isDark ? AppColor.neutral30 : AppColor.neutral20, size: 30).paddingOnly(top: 6, end: 0).onPress(() { + Navigator.of(context).push(CupertinoPageRoute(builder: (_) => UnReadChatList())); + }), + 8.width, + ], Stack( alignment: Alignment.topRight, children: [ diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 8d56bcf2..0978e161 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -48,6 +48,7 @@ import 'package:test_sa/modules/cx_module/chat/model/chat_login_response_model.d import 'package:uuid/uuid.dart'; import 'package:flutter/material.dart' as Material; +import 'api_client.dart'; import 'chat_api_client.dart'; import 'model/chat_attachment_model.dart'; import 'model/chat_participant_model.dart'; @@ -454,6 +455,19 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { return result; } + Future getUnReadMessages(String employeeId) async { + Response response = await ApiClient().getJsonForResponse( + "${URLs.unreadMessages}?retrieveAll=true", + headers: {'x-api-key': URLs.chatApiKey, 'x-employee-number': employeeId}, + ); + if (response.statusCode == 200) { + List data = jsonDecode(response.body)["response"]; + return data.map((elemet) => ChatAttachment.fromJson(elemet)).toList(); + } else { + return []; + } + } + // void updateUserChatStatus(List? args) { // dynamic items = args!.toList(); // for (var cItem in items[0]) { @@ -616,7 +630,6 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { } } - Future onAckSeenAsync(List? parameters) async { try { if (parameters == null || parameters.isEmpty) { diff --git a/lib/views/pages/user/notifications/unread_chat_list.dart b/lib/views/pages/user/notifications/unread_chat_list.dart new file mode 100644 index 00000000..eae702c0 --- /dev/null +++ b/lib/views/pages/user/notifications/unread_chat_list.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/cx_module/chat/chat_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; + +import '../../../widgets/loaders/no_data_found.dart'; + +class UnReadChatList extends StatelessWidget { + UnReadChatList({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const DefaultAppBar(title: "Unread Messages"), + body: FutureBuilder( + future: Provider.of(context, listen: false).getUnReadMessages(context.userProvider.user!.employeeId ?? context.userProvider.user!.username!), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center; + if (snapshot.data == null) return const NoDataFound().center; + + return SizedBox(); + + // List allAttachments = snapshot.data!.loanAttachments!; + // + // return Column( + // children: [ + // ListView( + // padding: const EdgeInsets.all(16), + // children: [ + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // StatusLabel( + // label: snapshot.data!.loanStatusName!, + // textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!), + // backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!), + // ), + // 1.width.expanded, + // Text( + // snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-", + // textAlign: TextAlign.end, + // style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + // ), + // ], + // ), + // 12.height, + // ...requesterDetails(context, snapshot.data!), + // const Divider().defaultStyle(context), + // ...requestDetails(context, snapshot.data!), + // const Divider().defaultStyle(context), + // ...assetDetails(context, snapshot.data!), + // const Divider().defaultStyle(context), + // ...installationDetails(context, snapshot.data!), + // const Divider().defaultStyle(context), + // ...doctorDetails(context, snapshot.data!), + // const Divider().defaultStyle(context), + // ...vendorDetails(context, snapshot.data!), + // if (allAttachments.isNotEmpty) ...[ + // const Divider().defaultStyle(context), + // Text( + // "Attachments".addTranslation, + // style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + // ), + // FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), + // ], + // ], + // ).toShadowContainer(context, padding: 12), + // ], + // ).expanded, + // // @todo ask backend to add loanTypeValue, so we can show or hide the buttons + // if (snapshot.data?.loanTypeName == "Standard" && + // context.userProvider.isEngineer && + // (snapshot.data?.loanStatusValue == 4 || snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) + // FooterActionButton.footerContainer( + // context: context, + // child: AppFilledButton( + // onPressed: () async { + // if (snapshot.data?.loanStatusValue == 4) { + // String comment = ""; + // context.showBottomSheet( + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // 'Estimated Date: ${snapshot.data?.installationEDD?.toAssetDetailsFormat ?? "-"}'.bodyText(context), + // 16.height, + // AppTextFormField( + // labelText: context.translation.comments, + // showSpeechToText: true, + // textInputType: TextInputType.multiline, + // labelStyle: AppTextStyles.textFieldLabelStyle, + // showWithoutDecoration: true, + // backgroundColor: context.isDarkNotListen ? AppColor.neutral20 : AppColor.neutral100, + // alignLabelWithHint: true, + // onChange: (text) { + // comment = text; + // }, + // ), + // 16.height, + // AppFilledButton( + // onPressed: () async { + // Utils.showLoading(context); + // LoanProvider loanProvider = Provider.of(context, listen: false); + // bool isSuccess = await loanProvider.loanWorkflowAction({ + // "userId": context.userProvider.user!.userID, + // "isFromMobile": true, + // "commentsFromMobile": comment, + // 'loanId': snapshot.data?.id, + // 'loanStatusId': snapshot.data?.loanStatusValue, + // }); + // Utils.hideLoading(context); + // if (isSuccess) { + // Navigator.pop(context, true); + // setState(() {}); + // } + // }, + // label: "Delivered"), + // ], + // ), + // title: "EDD & Comments", + // showCancelButton: true); + // return; + // } + // if (snapshot.data?.loanStatusValue == 5) { + // bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => InstallationFormView(loanData: snapshot.data))) ?? false; + // if (isSuccess) { + // setState(() {}); + // } + // return; + // } + // bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => PullOutDetailsPage(loanData: snapshot.data))) ?? false; + // if (isSuccess) { + // setState(() {}); + // } + // }, + // label: snapshot.data?.loanStatusValue == 4 + // ? "Delivered" + // : snapshot.data?.loanStatusValue == 5 + // ? 'Installation Report' + // : 'PullOut Report', + // ), + // ) + // ], + // ); + })); + } +} From c510c8e168c1eb24e9357bdce9f981051367139c Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 18 Feb 2026 15:17:07 +0300 Subject: [PATCH 6/7] unread message list added. --- lib/modules/cx_module/chat/chat_provider.dart | 25 ++- .../chat/model/unread_message_model.dart | 48 +++++ .../user/notifications/unread_chat_list.dart | 189 ++++++------------ 3 files changed, 122 insertions(+), 140 deletions(-) create mode 100644 lib/modules/cx_module/chat/model/unread_message_model.dart diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 0978e161..c060a12a 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -54,6 +54,7 @@ import 'model/chat_attachment_model.dart'; import 'model/chat_participant_model.dart'; import 'model/get_search_user_chat_model.dart'; import 'model/get_single_user_chat_list_model.dart'; +import 'model/unread_message_model.dart'; import 'model/user_chat_history_model.dart'; // import 'get_single_user_chat_list_model.dart'; @@ -455,15 +456,21 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { return result; } - Future getUnReadMessages(String employeeId) async { - Response response = await ApiClient().getJsonForResponse( - "${URLs.unreadMessages}?retrieveAll=true", - headers: {'x-api-key': URLs.chatApiKey, 'x-employee-number': employeeId}, - ); - if (response.statusCode == 200) { - List data = jsonDecode(response.body)["response"]; - return data.map((elemet) => ChatAttachment.fromJson(elemet)).toList(); - } else { + Future> getUnReadMessages(String employeeId) async { + // employeeId = "FMEngineer"; + + try { + Response response = await ApiClient().getJsonForResponse( + "${URLs.unreadMessages}?retrieveAll=true", + headers: {'x-api-key': URLs.chatApiKey, 'x-employee-number': employeeId}, + ); + if (response.statusCode == 200) { + List data = jsonDecode(response.body); + return data.map((elemet) => UnReadMessage.fromJson(elemet)).toList(); + } else { + return []; + } + } catch (error) { return []; } } diff --git a/lib/modules/cx_module/chat/model/unread_message_model.dart b/lib/modules/cx_module/chat/model/unread_message_model.dart new file mode 100644 index 00000000..da10d73e --- /dev/null +++ b/lib/modules/cx_module/chat/model/unread_message_model.dart @@ -0,0 +1,48 @@ +class UnReadMessage { + int? messageId; + int? conversationId; + String? senderEmployeeNumber; + String? senderUserName; + String? content; + String? createdAt; + String? moduleCode; + String? referenceId; + String? senderStatus; + + UnReadMessage( + {this.messageId, + this.conversationId, + this.senderEmployeeNumber, + this.senderUserName, + this.content, + this.createdAt, + this.moduleCode, + this.referenceId, + this.senderStatus}); + + UnReadMessage.fromJson(Map json) { + messageId = json['messageId']; + conversationId = json['conversationId']; + senderEmployeeNumber = json['senderEmployeeNumber']; + senderUserName = json['senderUserName']; + content = json['content']; + createdAt = json['createdAt']; + moduleCode = json['moduleCode']; + referenceId = json['referenceId']; + senderStatus = json['senderStatus']; + } + + Map toJson() { + final Map data = new Map(); + data['messageId'] = this.messageId; + data['conversationId'] = this.conversationId; + data['senderEmployeeNumber'] = this.senderEmployeeNumber; + data['senderUserName'] = this.senderUserName; + data['content'] = this.content; + data['createdAt'] = this.createdAt; + data['moduleCode'] = this.moduleCode; + data['referenceId'] = this.referenceId; + data['senderStatus'] = this.senderStatus; + return data; + } +} diff --git a/lib/views/pages/user/notifications/unread_chat_list.dart b/lib/views/pages/user/notifications/unread_chat_list.dart index eae702c0..3634180f 100644 --- a/lib/views/pages/user/notifications/unread_chat_list.dart +++ b/lib/views/pages/user/notifications/unread_chat_list.dart @@ -1,12 +1,16 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/notification/firebase_notification_manger.dart'; import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/cx_module/chat/chat_provider.dart'; +import 'package:test_sa/modules/cx_module/chat/model/unread_message_model.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; - -import '../../../widgets/loaders/no_data_found.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; class UnReadChatList extends StatelessWidget { UnReadChatList({Key? key}) : super(key: key); @@ -15,139 +19,62 @@ class UnReadChatList extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: const DefaultAppBar(title: "Unread Messages"), - body: FutureBuilder( + body: FutureBuilder>( future: Provider.of(context, listen: false).getUnReadMessages(context.userProvider.user!.employeeId ?? context.userProvider.user!.username!), - builder: (BuildContext context, AsyncSnapshot snapshot) { + builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center; - if (snapshot.data == null) return const NoDataFound().center; + if (snapshot.data?.isEmpty ?? true) return const NoDataFound().center; - return SizedBox(); + List messages = snapshot.data!; - // List allAttachments = snapshot.data!.loanAttachments!; - // - // return Column( - // children: [ - // ListView( - // padding: const EdgeInsets.all(16), - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // StatusLabel( - // label: snapshot.data!.loanStatusName!, - // textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!), - // backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!), - // ), - // 1.width.expanded, - // Text( - // snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-", - // textAlign: TextAlign.end, - // style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), - // ), - // ], - // ), - // 12.height, - // ...requesterDetails(context, snapshot.data!), - // const Divider().defaultStyle(context), - // ...requestDetails(context, snapshot.data!), - // const Divider().defaultStyle(context), - // ...assetDetails(context, snapshot.data!), - // const Divider().defaultStyle(context), - // ...installationDetails(context, snapshot.data!), - // const Divider().defaultStyle(context), - // ...doctorDetails(context, snapshot.data!), - // const Divider().defaultStyle(context), - // ...vendorDetails(context, snapshot.data!), - // if (allAttachments.isNotEmpty) ...[ - // const Divider().defaultStyle(context), - // Text( - // "Attachments".addTranslation, - // style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), - // ), - // FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), - // ], - // ], - // ).toShadowContainer(context, padding: 12), - // ], - // ).expanded, - // // @todo ask backend to add loanTypeValue, so we can show or hide the buttons - // if (snapshot.data?.loanTypeName == "Standard" && - // context.userProvider.isEngineer && - // (snapshot.data?.loanStatusValue == 4 || snapshot.data?.loanStatusValue == 5 || snapshot.data?.loanStatusValue == 8)) - // FooterActionButton.footerContainer( - // context: context, - // child: AppFilledButton( - // onPressed: () async { - // if (snapshot.data?.loanStatusValue == 4) { - // String comment = ""; - // context.showBottomSheet( - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // 'Estimated Date: ${snapshot.data?.installationEDD?.toAssetDetailsFormat ?? "-"}'.bodyText(context), - // 16.height, - // AppTextFormField( - // labelText: context.translation.comments, - // showSpeechToText: true, - // textInputType: TextInputType.multiline, - // labelStyle: AppTextStyles.textFieldLabelStyle, - // showWithoutDecoration: true, - // backgroundColor: context.isDarkNotListen ? AppColor.neutral20 : AppColor.neutral100, - // alignLabelWithHint: true, - // onChange: (text) { - // comment = text; - // }, - // ), - // 16.height, - // AppFilledButton( - // onPressed: () async { - // Utils.showLoading(context); - // LoanProvider loanProvider = Provider.of(context, listen: false); - // bool isSuccess = await loanProvider.loanWorkflowAction({ - // "userId": context.userProvider.user!.userID, - // "isFromMobile": true, - // "commentsFromMobile": comment, - // 'loanId': snapshot.data?.id, - // 'loanStatusId': snapshot.data?.loanStatusValue, - // }); - // Utils.hideLoading(context); - // if (isSuccess) { - // Navigator.pop(context, true); - // setState(() {}); - // } - // }, - // label: "Delivered"), - // ], - // ), - // title: "EDD & Comments", - // showCancelButton: true); - // return; - // } - // if (snapshot.data?.loanStatusValue == 5) { - // bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => InstallationFormView(loanData: snapshot.data))) ?? false; - // if (isSuccess) { - // setState(() {}); - // } - // return; - // } - // bool isSuccess = await Navigator.push(context, MaterialPageRoute(builder: (context) => PullOutDetailsPage(loanData: snapshot.data))) ?? false; - // if (isSuccess) { - // setState(() {}); - // } - // }, - // label: snapshot.data?.loanStatusValue == 4 - // ? "Delivered" - // : snapshot.data?.loanStatusValue == 5 - // ? 'Installation Report' - // : 'PullOut Report', - // ), - // ) - // ], - // ); + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemCount: messages.length, + separatorBuilder: (context, itemIndex) => const Divider().defaultStyle(context), + itemBuilder: (context, index) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + messages[index].senderUserName ?? "", + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, + ), + ).expanded, + 8.width, + Text( + messages[index].createdAt?.toServiceRequestCardFormat ?? "", + textAlign: TextAlign.right, + style: AppTextStyles.tinyFont.copyWith( + color: context.isDark ? AppColor.neutral20 : AppColor.neutral50, + ), + ), + ], + ), + Text( + messages[index].content ?? "", + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral10 : const Color(0xFF757575)), + ), + ], + ).onPress(() { + FirebaseNotificationManger.handleMessage(context, { + "transactionType": "17", + "requestType": "chat", + "moduleId": messages[index].moduleCode, + "requestNumber": messages[index].conversationId, + }); + }); + }, + ).toShadowContainer(context), + ); })); } } From 07c3775887237ae1a013b4130bb91433806990b7 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 18 Feb 2026 15:24:27 +0300 Subject: [PATCH 7/7] unread message list added. --- lib/views/pages/user/notifications/unread_chat_list.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/pages/user/notifications/unread_chat_list.dart b/lib/views/pages/user/notifications/unread_chat_list.dart index 3634180f..ed82f2f7 100644 --- a/lib/views/pages/user/notifications/unread_chat_list.dart +++ b/lib/views/pages/user/notifications/unread_chat_list.dart @@ -69,7 +69,7 @@ class UnReadChatList extends StatelessWidget { "transactionType": "17", "requestType": "chat", "moduleId": messages[index].moduleCode, - "requestNumber": messages[index].conversationId, + "requestNumber": messages[index].referenceId, }); }); },