From f257fbadfab6d5189cf132a3594d1a21b98d4254 Mon Sep 17 00:00:00 2001 From: "muhammad.abbasi" Date: Mon, 9 Sep 2024 14:46:27 +0300 Subject: [PATCH] new Api models and provider added --- assets/images/image_icon.svg | 3 + .../stapper_widget/components/base_step.dart | 186 ++++ .../components/base_step_delegate.dart | 51 ++ .../components/custom_line.dart | 80 ++ .../stapper_widget/custom_stepper.dart | 289 ++++++ lib/controllers/api_routes/api_manager.dart | 35 + lib/controllers/api_routes/urls.dart | 13 +- .../api/service_requests_provider.dart | 11 + lib/dashboard_latest/dashboard_provider.dart | 142 +++ lib/dashboard_latest/dashboard_view.dart | 11 +- .../widgets}/progress_fragment.dart | 11 +- .../widgets}/recent_activites_fragment.dart | 0 .../widgets}/request_category_fragment.dart | 2 +- .../widgets}/request_category_list.dart | 0 .../widgets}/requests_fragment.dart | 0 lib/l10n/app_ar.arb | 3 + lib/l10n/app_en.arb | 3 + lib/main.dart | 5 + lib/models/enums/user_types.dart | 1 + .../workorder/fix_remotely_model.dart | 35 + .../workorder/need_visit_model.dart | 27 + .../workorder/nurse_action_model.dart | 27 + lib/models/new_models/dashboardCount.dart | 75 ++ lib/models/new_models/dashboard_detail.dart | 140 +++ lib/models/new_models/workOrderDetail.dart | 834 ++++++++++++++++++ .../common_widgets/app_dashed_button.dart | 20 +- .../pages/land_page/dashboard_page.dart | 6 +- .../pages/land_page/requests_list_page.dart | 2 +- lib/providers/work_order/reason_provider.dart | 1 + .../request_detail_provider.dart | 450 ++++++++++ .../service_request_bottomsheet.dart | 5 +- .../views/components/request_detail_view.dart | 8 +- .../spare_part}/spare_part_request.dart | 14 +- .../components/asset_conditon_view.dart | 263 ++++++ .../components/attachments_view.dart | 40 + .../components/time_duration_view.dart | 120 +++ .../work_order/work_order_form_view.dart | 242 +++++ .../widgets/date_and_time/date_picker.dart | 35 +- .../widgets/date_and_time/time_picker.dart | 41 +- .../widgets/images/multi_image_picker.dart | 15 +- .../images/multi_image_picker_item.dart | 1 + pubspec.yaml | 1 - 42 files changed, 3173 insertions(+), 75 deletions(-) create mode 100644 assets/images/image_icon.svg create mode 100644 lib/common_widgets/stapper_widget/components/base_step.dart create mode 100644 lib/common_widgets/stapper_widget/components/base_step_delegate.dart create mode 100644 lib/common_widgets/stapper_widget/components/custom_line.dart create mode 100644 lib/common_widgets/stapper_widget/custom_stepper.dart create mode 100644 lib/dashboard_latest/dashboard_provider.dart rename lib/{new_views/pages/land_page/dashboard_fragments => dashboard_latest/widgets}/progress_fragment.dart (97%) rename lib/{new_views/pages/land_page/dashboard_fragments => dashboard_latest/widgets}/recent_activites_fragment.dart (100%) rename lib/{new_views/pages/land_page/dashboard_fragments => dashboard_latest/widgets}/request_category_fragment.dart (99%) rename lib/{new_views/pages/land_page/dashboard_fragments => dashboard_latest/widgets}/request_category_list.dart (100%) rename lib/{new_views/pages/land_page/dashboard_fragments => dashboard_latest/widgets}/requests_fragment.dart (100%) create mode 100644 lib/models/helper_data_models/workorder/fix_remotely_model.dart create mode 100644 lib/models/helper_data_models/workorder/need_visit_model.dart create mode 100644 lib/models/helper_data_models/workorder/nurse_action_model.dart create mode 100644 lib/models/new_models/dashboardCount.dart create mode 100644 lib/models/new_models/dashboard_detail.dart create mode 100644 lib/models/new_models/workOrderDetail.dart create mode 100644 lib/service_request_latest/request_detail_provider.dart rename lib/service_request_latest/views/{components => forms/spare_part}/spare_part_request.dart (95%) create mode 100644 lib/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart create mode 100644 lib/service_request_latest/views/forms/work_order/components/attachments_view.dart create mode 100644 lib/service_request_latest/views/forms/work_order/components/time_duration_view.dart create mode 100644 lib/service_request_latest/views/forms/work_order/work_order_form_view.dart diff --git a/assets/images/image_icon.svg b/assets/images/image_icon.svg new file mode 100644 index 00000000..9e54929a --- /dev/null +++ b/assets/images/image_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/common_widgets/stapper_widget/components/base_step.dart b/lib/common_widgets/stapper_widget/components/base_step.dart new file mode 100644 index 00000000..7ecea1b3 --- /dev/null +++ b/lib/common_widgets/stapper_widget/components/base_step.dart @@ -0,0 +1,186 @@ + +import 'package:flutter/material.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'base_step_delegate.dart'; +typedef OnStepReached = void Function(int index); + +enum StepShape { circle, rRectangle } + +enum BorderType { normal, dotted } + +class BaseStep extends StatelessWidget { + const BaseStep({ + Key key, + @required this.step, + @required this.isActive, + @required this.isFinished, + @required this.isAlreadyReached, + @required this.radius, + @required this.isUnreached, + @required this.activeStepBackgroundColor, + @required this.unreachedBackgroundColor, + @required this.activeStepBorderColor, + @required this.unreachedBorderColor, + @required this.activeTextColor, + @required this.activeIconColor, + @required this.unreachedTextColor, + @required this.unreachedIconColor, + @required this.padding, + @required this.stepRadius, + @required this.showStepBorder, + @required this.lineLength, + @required this.enabled, + }) : super(key: key); + final StepModel step; + final bool isActive; + final bool isUnreached; + final bool isFinished; + final bool isAlreadyReached ; + final double radius; + final Color activeStepBackgroundColor; + final Color unreachedBackgroundColor; + final Color activeStepBorderColor; + final Color unreachedBorderColor; + final Color activeTextColor; + final Color activeIconColor; + final Color unreachedTextColor; + final Color unreachedIconColor; + final double padding; + final double stepRadius; + final bool showStepBorder; + final double lineLength; + final bool enabled; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: (radius * 2) + (padding ?? 0), + height: radius * 2.5 + 25, + child: CustomMultiChildLayout( + delegate: BaseStepDelegate( + stepRadius: radius, + topTitle: step.topTitle, + direction: Axis.horizontal), + children: [ + LayoutId( + id: BaseStepElem.step, + child: Material( + color: Colors.transparent, + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(stepRadius ?? 0), + child: Container( + width: radius * 2, + height: radius * 2, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _handleColor(context, isFinished, isActive, + isAlreadyReached)), + alignment: Alignment.center, + child: _buildIcon(context), + ), + )), + LayoutId( + id: BaseStepElem.title, child: _buildStepTitle(context)) + ], + ), + ); + } + + Color _handleColor(BuildContext context, bool isFinished, bool isActive, + bool isAlreadyReached) { + if (isActive) { + return activeStepBackgroundColor ?? Colors.transparent; + } else { + if (isFinished) { + return AppColor.primary10; + } else if (isAlreadyReached) { + return AppColor.primary10; + } else { + return unreachedBackgroundColor ?? Colors.transparent; + } + } + } + + + Color _handleTitleColor(BuildContext context, bool isFinished, bool isActive, + bool isAlreadyReached) { + if (isActive) { + return activeTextColor ?? Theme.of(context).colorScheme.primary; + } else { + if (isFinished) { + return Colors.transparent; + } else if (isAlreadyReached) { + return Colors.transparent; + } else { + return unreachedTextColor ?? Colors.grey.shade400; + } + } + } + + + Widget _buildStepTitle(BuildContext context) { + return SizedBox( + width: (radius * 4.5) + + (padding ?? 0), + child: step.customTitle ?? + Text( + step.title ?? '', + maxLines: 3, + textAlign: TextAlign.center, + softWrap: false, + overflow: TextOverflow.visible, + style: Theme.of(context).textTheme.bodyMedium.copyWith( + color: _handleTitleColor( + context, isFinished, isActive, isAlreadyReached), + height: 1, + // fontSize: radius * 0.45, + ), + ), + ); + } + + SizedBox _buildIcon(BuildContext context) { + return SizedBox( + width: radius * 2, + height: radius * 2, + child: Center( + child: step.customStep ?? + Icon( + isActive && step.activeIcon != null + ? step.activeIcon.icon + : isFinished && step.finishIcon != null + ? step.finishIcon.icon + : step.icon.icon, + size: radius * 0.9, + color: Colors.white, + ), + ), + ); + } + +} +class StepModel { + final Icon icon; + final Icon finishIcon; + final Icon activeIcon; + final Widget customStep; + final String title; + final Widget customTitle; + final String lineText; + final Widget customLineWidget; + final bool topTitle; + final bool enabled; + const StepModel({ + this.icon, + this.finishIcon, + this.activeIcon, + this.title, + this.lineText, + this.customStep, + this.customTitle, + this.customLineWidget, + this.topTitle = false, + this.enabled = true, + }) : assert(icon != null || customStep != null); +} + diff --git a/lib/common_widgets/stapper_widget/components/base_step_delegate.dart b/lib/common_widgets/stapper_widget/components/base_step_delegate.dart new file mode 100644 index 00000000..43af00ee --- /dev/null +++ b/lib/common_widgets/stapper_widget/components/base_step_delegate.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +enum BaseStepElem { step, title } + +class BaseStepDelegate extends MultiChildLayoutDelegate { + final double stepRadius; + final Axis direction; + final bool topTitle; + + BaseStepDelegate({ + @required this.stepRadius, + @required this.direction, + this.topTitle = false, + }); + + @override + void performLayout(Size size) { + assert(hasChild(BaseStepElem.step)); + + layoutChild( + BaseStepElem.step, + BoxConstraints.loose( + size), // This just says that the child cannot be bigger than the whole layout. + ); + + positionChild( + BaseStepElem.step, + Offset( + (size.width - 2 * stepRadius) / 2, + direction == Axis.horizontal + ? 0 + : (size.height - 2 * stepRadius) / 2)); + + if (hasChild(BaseStepElem.title)) { + final Size titleSize = layoutChild( + BaseStepElem.title, + const BoxConstraints(), + ); + + Offset titleOffset = Offset(-titleSize.width / 2 + (size.width / 2), + topTitle ? -(stepRadius + titleSize.height) : (stepRadius * 2.35)); + + positionChild(BaseStepElem.title, titleOffset); + } + } + + @override + bool shouldRelayout(BaseStepDelegate oldDelegate) { + return false; + } +} diff --git a/lib/common_widgets/stapper_widget/components/custom_line.dart b/lib/common_widgets/stapper_widget/components/custom_line.dart new file mode 100644 index 00000000..2e29ac0d --- /dev/null +++ b/lib/common_widgets/stapper_widget/components/custom_line.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +class EasyLine extends StatelessWidget { + final double length; + final double width; + final Color color; + final double thickness; + final double spacing; + final LineType lineType; + final Axis axis; + + const EasyLine({ + key, + this.length = 50.0, + this.color = Colors.grey, + this.thickness = 3, + this.spacing = 3.0, + this.width = 2.0, + this.lineType = LineType.normal, + this.axis = Axis.horizontal, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: axis == Axis.horizontal + ? length + : thickness, + height: axis == Axis.vertical + ? length : thickness, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(100), + ), + ); + } +} + + +enum LineType { + normal, + dotted, + dashed, +} +class LineStyle { + final Color defaultLineColor; + final Color unreachedLineColor; + final Color activeLineColor; + final Color finishedLineColor; + final double lineLength; + final double lineThickness; + final double lineWidth; + final double lineSpace; + final LineType lineType; + final LineType unreachedLineType; + final Color progressColor; + final double progress; + const LineStyle({ + Key key, + this.lineType = LineType.dotted, + this.defaultLineColor, + this.unreachedLineColor, + this.activeLineColor, + this.finishedLineColor, + this.lineLength = 40, + this.lineWidth = 4, + this.lineThickness = 1, + this.lineSpace = 5, + this.unreachedLineType, + this.progressColor, + this.progress, + }) : assert( + progressColor == null || progress != null, + 'progress should be defined to use progressColor', + ), + assert( + progress == null || (progress >= 0 && progress <= 1), + 'progress value should be between 0 and 1', + ); +} \ No newline at end of file diff --git a/lib/common_widgets/stapper_widget/custom_stepper.dart b/lib/common_widgets/stapper_widget/custom_stepper.dart new file mode 100644 index 00000000..068c505c --- /dev/null +++ b/lib/common_widgets/stapper_widget/custom_stepper.dart @@ -0,0 +1,289 @@ +library easy_stepper; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'components/base_step.dart'; +import 'components/custom_line.dart'; +class CustomStepper extends StatefulWidget { + final List steps; + final OnStepReached onStepReached; + final Color unreachedStepBackgroundColor; + final Color unreachedStepTextColor; + final Color unreachedStepIconColor; + final Color unreachedStepBorderColor; + final BorderType unreachedStepBorderType; + final Color activeStepBackgroundColor; + final Color activeStepTextColor; + final Color activeStepIconColor; + final Color activeStepBorderColor; + final BorderType activeStepBorderType; + final Color finishedStepBackgroundColor; + final Color finishedStepBorderColor; + final Color finishedStepTextColor; + final Color finishedStepIconColor; + final BorderType finishedStepBorderType; + final double stepRadius; + final int activeStep; + final int maxReachedStep; + final Set reachedSteps; + final AlignmentGeometry alignment; + final double internalPadding; + final EdgeInsetsGeometry padding; + final bool titlesAreLargerThanSteps; + final Curve stepAnimationCurve; + final Duration stepAnimationDuration; + final double borderThickness; + final StepShape stepShape; + final double stepBorderRadius; + final bool showStepBorder; + final bool showScrollbar; + final bool fitWidth; + final LineStyle lineStyle; + + const CustomStepper({ + Key key, + @required this.activeStep, + @required this.steps, + this.reachedSteps, + this.maxReachedStep, + this.onStepReached, + this.unreachedStepBackgroundColor, + this.unreachedStepTextColor, + this.unreachedStepIconColor, + this.unreachedStepBorderColor, + this.activeStepTextColor, + this.activeStepIconColor=AppColor.primary10, + this.activeStepBackgroundColor=AppColor.primary10, + this.activeStepBorderColor=AppColor.primary10, + this.finishedStepBackgroundColor=AppColor.primary10, + this.finishedStepBorderColor, + this.finishedStepIconColor, + this.stepRadius = 20, + this.alignment = Alignment.center, + this.fitWidth = true, + this.showScrollbar = false, + this.padding=EdgeInsetsDirectional.zero, + this.titlesAreLargerThanSteps = false, + this.internalPadding = 8, + this.stepAnimationCurve = Curves.linear, + this.stepAnimationDuration = const Duration(seconds: 1), + this.borderThickness = 1, + this.stepShape = StepShape.circle, + this.stepBorderRadius, + this.unreachedStepBorderType, + this.activeStepBorderType, + this.finishedStepBorderType, + this.showStepBorder = false, + this.lineStyle, this.finishedStepTextColor, + }) : assert(maxReachedStep == null || reachedSteps == null, + 'only "maxReachedStep" or "reachedSteps" allowed'), + super(key: key); + + @override + State createState() => _CustomStepperState(); +} + +class _CustomStepperState extends State { + ScrollController _scrollController; + int _selectedIndex; + LineStyle lineStyle; + EdgeInsetsGeometry _padding; + + @override + void initState() { + lineStyle = widget.lineStyle ?? const LineStyle(); + _selectedIndex = widget.activeStep; + _scrollController = ScrollController(); + + _padding = const EdgeInsetsDirectional.all(10); + if (widget.steps.any((element) => element.topTitle)) { + _padding = _padding.add(const EdgeInsetsDirectional.only(top: 45)); + } + if (widget.titlesAreLargerThanSteps) { + _padding = _padding.add(EdgeInsetsDirectional.symmetric( + horizontal: lineStyle.lineLength / 2)); + } + if (widget.padding != null) { + _padding.add(widget.padding); + } + + super.initState(); + } + + @override + void didUpdateWidget(CustomStepper oldWidget) { + super.didUpdateWidget(oldWidget); + + // Verify that the active step falls within a valid range. + if (widget.activeStep >= 0 && widget.activeStep < widget.steps.length) { + _selectedIndex = widget.activeStep; + } + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + /// Controls the step scrolling. + void _afterLayout(_) { + for (int i = 0; i < widget.steps.length; i++) { + _scrollController.animateTo( + i * + ((widget.stepRadius * 2) + + widget.internalPadding + + lineStyle.lineLength), + duration: widget.stepAnimationDuration, + curve: widget.stepAnimationCurve, + ); + + if (_selectedIndex == i) break; + } + } + + @override + Widget build(BuildContext context) { + lineStyle = widget.lineStyle ?? const LineStyle(); + + return Align( + alignment: widget.alignment, + child: NotificationListener( + onNotification: (OverscrollIndicatorNotification overscroll) { + overscroll.disallowIndicator(); + return false; + }, + child: FittedBox( + fit: widget.fitWidth ? BoxFit.fitWidth : BoxFit.none, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: _buildEasySteps(), + ), + ) + ), + ); + } + + List _buildEasySteps() { + return List.generate(widget.steps.length, (index) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildStep(index), + _buildLine(index, Axis.horizontal), + ], + ); + }); + } + + BaseStep _buildStep(int index) { + final step = widget.steps[index]; + return BaseStep( + step: step, + radius: widget.stepRadius, + isActive: index == widget.activeStep, + isFinished: widget.reachedSteps != null + ? index < widget.activeStep && widget.reachedSteps.contains(index) + : index < widget.activeStep, + isUnreached: index > widget.activeStep, + isAlreadyReached: widget.reachedSteps != null + ? widget.reachedSteps.contains(index) + : widget.maxReachedStep != null + ? index <= widget.maxReachedStep + : false, + activeStepBackgroundColor: widget.activeStepBackgroundColor, + activeStepBorderColor: widget.activeStepBorderColor, + activeTextColor: widget.activeStepTextColor, + activeIconColor: widget.activeStepIconColor, + unreachedBackgroundColor: widget.unreachedStepBackgroundColor, + unreachedBorderColor: widget.unreachedStepBorderColor, + unreachedTextColor: widget.unreachedStepTextColor, + unreachedIconColor: widget.unreachedStepIconColor, + padding: max(widget.internalPadding, 0), + stepRadius: widget.stepBorderRadius, + showStepBorder: widget.showStepBorder, + lineLength: lineStyle.lineLength, + enabled: widget.steps[index].enabled, + ); + } + + // BorderType _handleBorderType(int index) { + // if (index == widget.activeStep) { + // //Active Step + // return widget.activeStepBorderType ?? widget.defaultStepBorderType; + // } else if (index > widget.activeStep) { + // //Unreached Step + // return widget.unreachedStepBorderType ?? widget.defaultStepBorderType; + // } else if (index < widget.activeStep) { + // //Finished Step + // return widget.finishedStepBorderType ?? widget.defaultStepBorderType; + // } else { + // return widget.defaultStepBorderType; + // } + // } + + Color _getLineColor(int index) { + Color preferredColor; + if (index == widget.activeStep) { + //Active Step + preferredColor = lineStyle.activeLineColor; + } else if (index > widget.activeStep) { + //Unreached Step + preferredColor = lineStyle.unreachedLineColor; + } else if (index < widget.activeStep) { + //Finished Step + preferredColor = lineStyle.finishedLineColor; + } + + return preferredColor ?? + lineStyle.defaultLineColor ?? + Theme.of(context).colorScheme.primary; + } + + Widget _buildLine(int index, Axis axis) { + return index < widget.steps.length - 1 + ? Column( + children: [ + Padding( + padding: EdgeInsets.only( + top: axis == Axis.horizontal + ? (widget.stepRadius - lineStyle.lineThickness) + : 0, + ), + child: _buildBaseLine(index, axis), + ), + if (axis == Axis.horizontal && + widget.steps[index].lineText != null) ...[ + const SizedBox(height: 5), + SizedBox( + width: lineStyle.lineLength, + child: widget.steps[index].customLineWidget ?? + Text( + widget.steps[index].lineText, + maxLines: 3, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelSmall, + ), + ), + ], + ], + ) + : const Offstage(); + } + + EasyLine _buildBaseLine(int index, Axis axis) { + return EasyLine( + length: lineStyle.lineLength, + color: _getLineColor(index), + thickness: lineStyle.lineThickness, + spacing: lineStyle.lineSpace, + width: lineStyle.lineWidth, + axis: axis, + lineType: + index > widget.activeStep - 1 && lineStyle.unreachedLineType != null + ? lineStyle.unreachedLineType + : lineStyle.lineType, + ); + } +} diff --git a/lib/controllers/api_routes/api_manager.dart b/lib/controllers/api_routes/api_manager.dart index e8464923..91728fed 100644 --- a/lib/controllers/api_routes/api_manager.dart +++ b/lib/controllers/api_routes/api_manager.dart @@ -91,6 +91,41 @@ class ApiManager { return response; } + Future postWithOutBody( + String url, { + Map headers, + }) async { + headers ??= {}; + + headers.addAll(_headers); + + Uri _url = Uri.parse(url); + if (!kReleaseMode) { + print("URL:$_url"); + print("Headers:$headers"); + } + + var request = http.Request('POST', _url); + request.headers.addAll(headers); + + http.StreamedResponse _streamedResponse = await request.send(); + http.Response response = await http.Response.fromStream(_streamedResponse); + try { + if (response.statusCode == 401) { + showLoginDialog(); + } else { + if (jsonDecode(response.body) is Map) { + final message = jsonDecode(response.body)["message"]; + if (message != null && message.toString().isNotEmpty) { + Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG); + } + } + } + } catch (ex) {} + + return response; + } + Future put( String url, { diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 11e73fde..03e76360 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -25,7 +25,18 @@ class URLs { static get getSites => "$_baseUrl/Customer/GetCustomers"; // get static get getSitesAutoComplete => "$_baseUrl/Customer/GetCustomersAutoComplete"; // get static get getSiteAutoCompleteWithoutConditionSites => "$_baseUrl/Customer/GetCustomersAutoCompleteWithoutConditionSites"; // get - + static get nurseDashboardCountUrl=> '$_baseUrl/ServiceRequest/GetDashboardNurseCount'; + static get nurseDashboardDetailsUrl=> '$_baseUrl/ServiceRequest/GetDashboardNurseDetails'; + static get nurseConfirmReopenUrl=> '$_baseUrl/ServiceRequest/NurseConfirmReopen'; + static get nurseConfirmCloseUrl=> '$_baseUrl/ServiceRequest/NurseConfirmClose'; + static get engineerDashboardCountUrl=> '$_baseUrl/ServiceRequest/GetDashboardEngineerCount'; + static get engineerDashboardDetailsUrl=> '$_baseUrl/ServiceRequest/GetDashboardEngineerDetails'; + static get engineerAcceptUrl=> '$_baseUrl/ServiceRequest/EngineerAccept'; + static get engineerRejectUrl=> '$_baseUrl/ServiceRequest/EngineerReject'; + static get engineerFixRemotlyUrl=> '$_baseUrl/ServiceRequest/EngineerFixRemotly'; + static get engineerNeedVisitUrl=> '$_baseUrl/ServiceRequest/EngineerNeedVisit'; + static get getWorkOrderByIdUrl=> '$_baseUrl/ServiceRequest/GetWorkOrderById'; + static get assignEngineerToWorkOrderUrl=> '$_baseUrl/ServiceRequest/AssignEngineerToWorkOrder'; static get getDepartments => "$_baseUrl/Customer/GetDepartmentLookup"; // get static get getAssets => "$_baseUrl/Asset/GetAssets"; // get static get getAssetById => "$_baseUrl/Asset/GetAssetById?assetId="; // get diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index aa865b04..5d8e15e6 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -62,6 +62,15 @@ class ServiceRequestsProvider extends ChangeNotifier { } // list of user requests List serviceRequests; List workOrders = []; + SearchWorkOrder _formWorkOrder=SearchWorkOrder(); + + SearchWorkOrder get formWorkOrder => _formWorkOrder; + + set formWorkOrder(SearchWorkOrder value) { + _formWorkOrder = value; + notifyListeners(); + } + List _sparePartList = []; @@ -81,9 +90,11 @@ class ServiceRequestsProvider extends ChangeNotifier { set currentSelectedRequest(ServiceRequest value) { _currentSelectedRequest = value; + print('notify listner for current selected request Called...'); notifyListeners(); } + // when requests in-process _loading = true // done _loading = true // failed _loading = false diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart new file mode 100644 index 00000000..715ff72a --- /dev/null +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -0,0 +1,142 @@ +import 'dart:convert'; +import 'package:flutter/widgets.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/models/enums/user_types.dart'; +import 'package:test_sa/models/new_models/dashboardCount.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; + +class DashBoardProvider extends ChangeNotifier { + bool isAllCountLoading = false; + bool isLoading = false; + bool _isDetailLoading = false; + + int _status =0; + + + int get status => _status; + + set status(int value) { + _status = value; + notifyListeners(); + } + + int _currentListIndex = 0; + + int get currentListIndex => _currentListIndex; + + set currentListIndex(int value) { + _currentListIndex = value; + notifyListeners(); + } + + int stateCode; + + DashboardCount dashboardCount; + + DashboardDetail _requestDetailList; + + DashboardDetail get requestDetailList => _requestDetailList; + + set requestDetailList(DashboardDetail value) { + _requestDetailList = value; + notifyListeners(); + } + + final pageItemNumber = 10; + int pageNum = 1; + bool nextPage = true; + + void reset() { + pageNum = 1; + nextPage = true; + stateCode = null; + } + + + Future getDashBoardCount({@required UsersTypes usersType}) async { + isAllCountLoading = true; + String url=''; + if(usersType==UsersTypes.engineer){ + url = URLs.engineerDashboardCountUrl; + } + if(usersType==UsersTypes.nurse){ + url=URLs.nurseDashboardCountUrl; + } + Response response; + try { + response = await ApiManager.instance.postWithOutBody(url); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + dashboardCount = DashboardCount.fromJson(json.decode(response.body)); + } + notifyListeners(); + isAllCountLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isAllCountLoading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } + + + Future getRequestDetail({bool showLoader = true,UsersTypes usersType, int status}) async { + isDetailLoading =showLoader; + Response response; + String url=''; + if(usersType==UsersTypes.engineer){ + url = URLs.engineerDashboardDetailsUrl; + } + if(usersType==UsersTypes.nurse){ + url=URLs.nurseDashboardDetailsUrl; + } + try { + Map body = { + "statusValue": status, + "pageNumber": pageNum, + "pageSize" : pageItemNumber, + }; + response = await ApiManager.instance.post(url, body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + if (requestDetailList == null) { + requestDetailList = DashboardDetail.fromJson(json.decode(response.body)); + isDetailLoading = false; + notifyListeners(); + } else { + requestDetailList.data.addAll(DashboardDetail.fromJson(json.decode(response.body)["data"][0]).data); + isDetailLoading = false; + notifyListeners(); + } + if (requestDetailList.data.length >= pageItemNumber) { + nextPage = true; + } else { + nextPage = false; + } + notifyListeners(); + } else { + requestDetailList = null; + } + isDetailLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isDetailLoading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } + + bool get isDetailLoading => _isDetailLoading; + + set isDetailLoading(bool value) { + _isDetailLoading = value; + notifyListeners(); + } + +} diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 1a544913..56b2b048 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -9,6 +10,7 @@ import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/notifications_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; +import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; @@ -16,11 +18,12 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/user.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/progress_fragment.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/requests_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart'; +import 'package:test_sa/service_request_latest/request_detail_provider.dart'; import 'package:test_sa/utilities/request_utils.dart'; -import '../new_views/pages/land_page/dashboard_fragments/request_category_fragment.dart'; +import 'widgets/request_category_fragment.dart'; class DashboardView extends StatefulWidget { final VoidCallback onDrawerPress; @@ -55,10 +58,12 @@ class _DashboardViewState extends State { scheduleMicrotask(() async { userProvider = Provider.of(context, listen: false); settingProvider = Provider.of(context, listen: false); + RequestDetailProvider requestDetailProvider = Provider.of(context, listen: false); allRequestsProvider = Provider.of(context, listen: false); notificationsProvider = Provider.of(context, listen: false); user = userProvider.user; await getAllRequests(); + await requestDetailProvider.engineerRejectWorkOrder(id: '3',feedBack: 'Abcdef'); if (isFCM) { FirebaseNotificationManger.initialized(context); diff --git a/lib/new_views/pages/land_page/dashboard_fragments/progress_fragment.dart b/lib/dashboard_latest/widgets/progress_fragment.dart similarity index 97% rename from lib/new_views/pages/land_page/dashboard_fragments/progress_fragment.dart rename to lib/dashboard_latest/widgets/progress_fragment.dart index 409d9714..6c316a10 100644 --- a/lib/new_views/pages/land_page/dashboard_fragments/progress_fragment.dart +++ b/lib/dashboard_latest/widgets/progress_fragment.dart @@ -159,8 +159,8 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; -import '../../../../controllers/providers/api/user_provider.dart'; -import '../../../../models/enums/user_types.dart'; +import '../../controllers/providers/api/user_provider.dart'; +import '../../models/enums/user_types.dart'; class ProgressFragment extends StatelessWidget { ProgressFragment({Key key}) : super(key: key); @@ -186,13 +186,6 @@ class ProgressFragment extends StatelessWidget { if (isCurrentUserNotEngineer) { chartData.insert(1, ChartData('Open', snapshot.openRequests?.total?.count?.toDouble() ?? 0.0, AppColor.blueStatus(context))); } - print('conditon value is ${ - snapshot.isInProgressLoading - }');print('conditon value is ${ - snapshot.isCompleteLoading - }');print('conditon value is ${ - snapshot.isOpenLoading - }'); return Column( children: [ diff --git a/lib/new_views/pages/land_page/dashboard_fragments/recent_activites_fragment.dart b/lib/dashboard_latest/widgets/recent_activites_fragment.dart similarity index 100% rename from lib/new_views/pages/land_page/dashboard_fragments/recent_activites_fragment.dart rename to lib/dashboard_latest/widgets/recent_activites_fragment.dart diff --git a/lib/new_views/pages/land_page/dashboard_fragments/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart similarity index 99% rename from lib/new_views/pages/land_page/dashboard_fragments/request_category_fragment.dart rename to lib/dashboard_latest/widgets/request_category_fragment.dart index fad13590..ccbffb94 100644 --- a/lib/new_views/pages/land_page/dashboard_fragments/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -83,7 +83,7 @@ import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/user.dart'; import 'package:test_sa/new_views/common_widgets/tab_button.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/request_category_list.dart'; +import 'package:test_sa/dashboard_latest/widgets/request_category_list.dart'; import 'package:test_sa/utilities/request_utils.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; diff --git a/lib/new_views/pages/land_page/dashboard_fragments/request_category_list.dart b/lib/dashboard_latest/widgets/request_category_list.dart similarity index 100% rename from lib/new_views/pages/land_page/dashboard_fragments/request_category_list.dart rename to lib/dashboard_latest/widgets/request_category_list.dart diff --git a/lib/new_views/pages/land_page/dashboard_fragments/requests_fragment.dart b/lib/dashboard_latest/widgets/requests_fragment.dart similarity index 100% rename from lib/new_views/pages/land_page/dashboard_fragments/requests_fragment.dart rename to lib/dashboard_latest/widgets/requests_fragment.dart diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index f44a8a27..35bf5d60 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -273,6 +273,9 @@ "assets" : "الأصول", "contact": "اتصال", "feedBack": "تعليقات", + "timeAndDuration": "الوقت والمدة", + "assetsCondition": "حالة الأصول", + "attachmentsAcknowledge": "إقرار & المرفقات", "welcome" : "مرحبا،", "openWhatsapp" : "الإنتقال الى الواتس اب", "callUs" : "إتصل بنا", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0f2d211e..275ff9a3 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -245,6 +245,9 @@ "contact": "Contact", "welcome" : "Welcome,", "openWhatsapp" : "Open WhatsApp", + "timeAndDuration": "Time and Duration", + "assetsCondition": "Asset’s Condition", + "attachmentsAcknowledge": "Attachments & Acknowledge", "callUs" : "Call Us", "liveChat" : "Live Chat", "feedBack" : "Feedback", diff --git a/lib/main.dart b/lib/main.dart index 3b3ab99a..0b242c64 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -77,6 +77,7 @@ import 'package:test_sa/providers/work_order/reason_provider.dart'; import 'package:test_sa/providers/work_order/service_type_provider.dart'; import 'package:test_sa/providers/work_order/supplier_engineer_provider.dart'; import 'package:test_sa/providers/work_order/vendor_provider.dart'; +import 'package:test_sa/service_request_latest/request_detail_provider.dart'; import 'package:test_sa/views/pages/device_transfer/asset_filter_screen.dart'; import 'package:test_sa/views/pages/device_transfer/asset_search_screen.dart'; import 'package:test_sa/views/pages/device_transfer/request_device_transfer.dart'; @@ -104,6 +105,7 @@ import 'package:test_sa/views/widgets/hospitals/single_hospital_picker.dart'; import 'controllers/providers/api/gas_refill_comments.dart'; import 'controllers/providers/api/user_provider.dart'; import 'controllers/providers/settings/setting_provider.dart'; +import 'dashboard_latest/dashboard_provider.dart'; import 'new_views/pages/new_gas_refill_request_page.dart'; import 'providers/service_request_providers/loan_availability_provider.dart'; @@ -158,6 +160,9 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => ServiceRequestsProvider()), ChangeNotifierProvider(create: (_) => DepartmentsProvider()), ChangeNotifierProvider(create: (_) => NotificationsProvider()), + //new providers according to new Api's.. + ChangeNotifierProvider(create: (_) => DashBoardProvider()), + ChangeNotifierProvider(create: (_) => RequestDetailProvider()), // ChangeNotifierProvider(create: (_) => PreventiveMaintenanceVisitsProvider()), ChangeNotifierProvider(create: (_) => PpmProvider()), ChangeNotifierProvider(create: (_) => PartsProvider()), diff --git a/lib/models/enums/user_types.dart b/lib/models/enums/user_types.dart index 41df2134..e84e2493 100644 --- a/lib/models/enums/user_types.dart +++ b/lib/models/enums/user_types.dart @@ -1,4 +1,5 @@ enum UsersTypes { engineer, // 0 normal_user, // 1 + nurse, // 1 } diff --git a/lib/models/helper_data_models/workorder/fix_remotely_model.dart b/lib/models/helper_data_models/workorder/fix_remotely_model.dart new file mode 100644 index 00000000..75523cb5 --- /dev/null +++ b/lib/models/helper_data_models/workorder/fix_remotely_model.dart @@ -0,0 +1,35 @@ +class FixRemotely { + int workOrderId; + DateTime startDate; + DateTime endDate; + int workingHour; + String comment; + + FixRemotely({ + this.workOrderId, + this.startDate, + this.endDate, + this.workingHour, + this.comment, + }); + + factory FixRemotely.fromJson(Map json) { + return FixRemotely( + workOrderId: json['workOrderId'], + startDate: DateTime.parse(json['startDate']), + endDate: DateTime.parse(json['endDate']), + workingHour: json['workingHour'], + comment: json['comment'], + ); + } + + Map toJson() { + return { + 'workOrderId': workOrderId, + 'startDate': startDate.toIso8601String(), + 'endDate': endDate.toIso8601String(), + 'workingHour': workingHour, + 'comment': comment, + }; + } +} diff --git a/lib/models/helper_data_models/workorder/need_visit_model.dart b/lib/models/helper_data_models/workorder/need_visit_model.dart new file mode 100644 index 00000000..d345af95 --- /dev/null +++ b/lib/models/helper_data_models/workorder/need_visit_model.dart @@ -0,0 +1,27 @@ +class NeedVisit { + int workOrderId; + DateTime visitDate; + String comment; + + NeedVisit({ + this.workOrderId, + this.visitDate, + this.comment, + }); + + factory NeedVisit.fromJson(Map json) { + return NeedVisit( + workOrderId: json['workOrderId'], + visitDate: DateTime.parse(json['visitDate']), + comment: json['comment'], + ); + } + + Map toJson() { + return { + 'workOrderId': workOrderId, + 'visitDate': visitDate.toIso8601String(), + 'comment': comment, + }; + } +} diff --git a/lib/models/helper_data_models/workorder/nurse_action_model.dart b/lib/models/helper_data_models/workorder/nurse_action_model.dart new file mode 100644 index 00000000..86647e7f --- /dev/null +++ b/lib/models/helper_data_models/workorder/nurse_action_model.dart @@ -0,0 +1,27 @@ +class NurseActionModel { + int workOrderId; + String feedback; + String signatureNurse; + + NurseActionModel({ + this.workOrderId, + this.feedback, + this.signatureNurse, + }); + + factory NurseActionModel.fromJson(Map json) { + return NurseActionModel( + workOrderId: json['workOrderId'], + feedback: json['feedback'], + signatureNurse: json['signatureNurse'], + ); + } + + Map toJson() { + return { + 'workOrderId': workOrderId, + 'feedback': feedback, + 'signatureNurse': signatureNurse, + }; + } +} diff --git a/lib/models/new_models/dashboardCount.dart b/lib/models/new_models/dashboardCount.dart new file mode 100644 index 00000000..7fddba76 --- /dev/null +++ b/lib/models/new_models/dashboardCount.dart @@ -0,0 +1,75 @@ +class DashboardCount { + Data data; + String message; + String title; + String innerMessage; + int responseCode; + bool isSuccess; + + DashboardCount( + {this.data, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess}); + + DashboardCount.fromJson(Map json) { + data = json['data'] != null ? Data.fromJson(json['data']) : null; + message = json['message']; + title = json['title']; + innerMessage = json['innerMessage']; + responseCode = json['responseCode']; + isSuccess = json['isSuccess']; + } + + Map toJson() { + final Map data = {}; + if (this.data != null) { + data['data'] = this.data.toJson(); + } + data['message'] = message; + data['title'] = title; + data['innerMessage'] = innerMessage; + data['responseCode'] = responseCode; + data['isSuccess'] = isSuccess; + return data; + } +} + +class Data { + int countOpen; + int countInprogress; + int countComplete; + int countAcknowledge; + int countHighPriority; + int countOverdue; + + Data( + {this.countOpen, + this.countInprogress, + this.countComplete, + this.countAcknowledge, + this.countHighPriority, + this.countOverdue}); + + Data.fromJson(Map json) { + countOpen = json['countOpen']??0; + countInprogress = json['countInprogress']??0; + countComplete = json['countComplete']??0; + countAcknowledge = json['countAcknowledge']??0; + countHighPriority = json['countHighPriority']??0; + countOverdue = json['countOverdue']??0; + } + + Map toJson() { + final Map data = {}; + data['countOpen'] = countOpen; + data['countInprogress'] = countInprogress; + data['countComplete'] = countComplete; + data['countAcknowledge'] = countAcknowledge; + data['countHighPriority'] = countHighPriority; + data['countOverdue'] = countOverdue; + return data; + } +} \ No newline at end of file diff --git a/lib/models/new_models/dashboard_detail.dart b/lib/models/new_models/dashboard_detail.dart new file mode 100644 index 00000000..110488b9 --- /dev/null +++ b/lib/models/new_models/dashboard_detail.dart @@ -0,0 +1,140 @@ +class DashboardDetail { + List data; + int totalRows; + String message; + String title; + String innerMessage; + int responseCode; + bool isSuccess; + + DashboardDetail( + {this.data, + this.totalRows, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess}); + + DashboardDetail.fromJson(Map json) { + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data.add(Data.fromJson(v)); + }); + } + totalRows = json['totalRows']; + message = json['message']; + title = json['title']; + innerMessage = json['innerMessage']; + responseCode = json['responseCode']; + isSuccess = json['isSuccess']; + } + + Map toJson() { + final Map data = {}; + if (this.data != null) { + data['data'] = this.data.map((v) => v.toJson()).toList(); + } + data['totalRows'] = totalRows; + data['message'] = message; + data['title'] = title; + data['innerMessage'] = innerMessage; + data['responseCode'] = responseCode; + data['isSuccess'] = isSuccess; + return data; + } +} + +class Data { + int id; + String typeTransaction; + String transactionDate; + String statusName; + String priorityName; + bool isHighPriority; + String assetName; + String assetNumber; + String requestTypeName; + String requestNo; + + Data( + {this.id, + this.typeTransaction, + this.transactionDate, + this.statusName, + this.priorityName, + this.isHighPriority, + this.assetName, + this.assetNumber, + this.requestTypeName, + this.requestNo}); + + Data.fromJson(Map json) { + id = json['id']; + typeTransaction = json['typeTransaction']; + transactionDate = json['transactionDate']; + statusName = json['statusName']; + priorityName = json['priorityName']; + isHighPriority = json['isHighPriority']; + assetName = json['assetName']; + assetNumber = json['assetNumber']; + requestTypeName = json['requestTypeName']; + requestNo = json['requestNo']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['typeTransaction'] = typeTransaction; + data['transactionDate'] = transactionDate; + data['statusName'] = statusName; + data['priorityName'] = priorityName; + data['isHighPriority'] = isHighPriority; + data['assetName'] = assetName; + data['assetNumber'] = assetNumber; + data['requestTypeName'] = requestTypeName; + data['requestNo'] = requestNo; + return data; + } +} +class CommonResponseModel{ + bool data; + String message; + String title; + String innerMessage; + int responseCode; + bool isSuccess; + + CommonResponseModel({ + this.data, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess, + }); + + factory CommonResponseModel.fromJson(Map json) { + return CommonResponseModel( + data: json['data'], + message: json['message'] ?? '', + title: json['title'], + innerMessage: json['innerMessage'], + responseCode: json['responseCode'], + isSuccess: json['isSuccess'], + ); + } + + Map toJson() { + return { + 'data': data, + 'message': message, + 'title': title, + 'innerMessage': innerMessage, + 'responseCode': responseCode, + 'isSuccess': isSuccess, + }; + } +} + diff --git a/lib/models/new_models/workOrderDetail.dart b/lib/models/new_models/workOrderDetail.dart new file mode 100644 index 00000000..2841f195 --- /dev/null +++ b/lib/models/new_models/workOrderDetail.dart @@ -0,0 +1,834 @@ +class WorkOrderDetail { + WorkOrder data; + String message; + String title; + String innerMessage; + int responseCode; + bool isSuccess; + + WorkOrderDetail({ + this.data, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess, + }); + + factory WorkOrderDetail.fromJson(Map json) { + return WorkOrderDetail( + data: WorkOrder.fromJson(json['data']), + message: json['message'], + title: json['title'], + innerMessage: json['innerMessage'], + responseCode: json['responseCode'], + isSuccess: json['isSuccess'], + ); + } + Map toJson() { + return { + 'data': data?.toJson(), + 'message': message, + 'title': title, + 'innerMessage': innerMessage, + 'responseCode': responseCode, + 'isSuccess': isSuccess, + }; + } +} + +class WorkOrder { + String workOrderNo; + WorkOrderCreatedBy workOrderCreatedBy; + DateTime requestedDate; + Asset asset; + AssetGroup assetGroup; + Manufacturer manufacturer; + Model model; + AssetNDModel assetNDModel; + Site site; + Building building; + Floor floor; + Department department; + dynamic room; + AssetType assetType; + AssignedEmployee assignedEmployee; + dynamic lastActivityStatus; + Status status; + NextStep nextStep; + dynamic assetVerificationType; + List workOrderContactPerson; + EquipmentStatus equipmentStatus; + Priority priority; + RequestedThrough requestedThrough; + TypeOfRequest typeofRequest; + dynamic loanAvailablity; + dynamic assetLoan; + dynamic safety; + ProblemDescription problemDescription; + String comments; + String voiceNote; + List workOrderAttachments; + dynamic returnToService; + dynamic serviceType; + dynamic failureReasone; + dynamic solution; + dynamic totalWorkingHours; + List workOrderHistory; + List activityMaintenances; + List activitySpareParts; + List activityAssetToBeRetireds; + + WorkOrder({ + this.workOrderNo, + this.workOrderCreatedBy, + this.requestedDate, + this.asset, + this.assetGroup, + this.manufacturer, + this.model, + this.assetNDModel, + this.site, + this.building, + this.floor, + this.department, + this.room, + this.assetType, + this.assignedEmployee, + this.lastActivityStatus, + this.status, + this.nextStep, + this.assetVerificationType, + this.workOrderContactPerson, + this.equipmentStatus, + this.priority, + this.requestedThrough, + this.typeofRequest, + this.loanAvailablity, + this.assetLoan, + this.safety, + this.problemDescription, + this.comments, + this.voiceNote, + this.workOrderAttachments, + this.returnToService, + this.serviceType, + this.failureReasone, + this.solution, + this.totalWorkingHours, + this.workOrderHistory, + this.activityMaintenances, + this.activitySpareParts, + this.activityAssetToBeRetireds, + }); + + factory WorkOrder.fromJson(Map json) { + return WorkOrder( + workOrderNo: json['workOrderNo'], + workOrderCreatedBy: WorkOrderCreatedBy.fromJson(json['workOrderCreatedBy']), + requestedDate: DateTime.parse(json['requestedDate']), + asset: Asset.fromJson(json['asset']), + assetGroup: AssetGroup.fromJson(json['assetGroup']), + manufacturer: Manufacturer.fromJson(json['manufacturer']), + model: Model.fromJson(json['model']), + assetNDModel: AssetNDModel.fromJson(json['assetNDModel']), + site: Site.fromJson(json['site']), + building: Building.fromJson(json['building']), + floor: Floor.fromJson(json['floor']), + department: Department.fromJson(json['department']), + room: json['room'], + assetType: AssetType.fromJson(json['assetType']), + assignedEmployee: AssignedEmployee.fromJson(json['assignedEmployee']), + lastActivityStatus: json['lastActivityStatus'], + status: Status.fromJson(json['status']), + nextStep: NextStep.fromJson(json['nextStep']), + assetVerificationType: json['assetVerificationType'], + workOrderContactPerson: (json['workOrderContactPerson'] as List) + .map((i) => WorkOrderContactPerson.fromJson(i)) + .toList(), + equipmentStatus: EquipmentStatus.fromJson(json['equipmentStatus']), + priority: Priority.fromJson(json['priority']), + requestedThrough: RequestedThrough.fromJson(json['requestedThrough']), + typeofRequest: TypeOfRequest.fromJson(json['typeofRequest']), + loanAvailablity: json['loanAvailablity'], + assetLoan: json['assetLoan'], + safety: json['safety'], + problemDescription: ProblemDescription.fromJson(json['problemDescription']), + comments: json['comments'], + voiceNote: json['voiceNote'], + workOrderAttachments: json['workOrderAttachments'] as List, + returnToService: json['returnToService'], + serviceType: json['serviceType'], + failureReasone: json['failureReasone'], + solution: json['solution'], + totalWorkingHours: json['totalWorkingHours'], + workOrderHistory: (json['workOrderHistory'] as List) + .map((i) => WorkOrderHistory.fromJson(i)) + .toList(), + activityMaintenances: json['activityMaintenances'] as List, + activitySpareParts: json['activitySpareParts'] as List, + activityAssetToBeRetireds: json['activityAssetToBeRetireds'] as List, + ); + } + Map toJson() { + return { + 'workOrderNo': workOrderNo, + 'workOrderCreatedBy': workOrderCreatedBy.toJson(), + 'requestedDate': requestedDate.toIso8601String(), + 'asset': asset.toJson(), + 'assetGroup': assetGroup.toJson(), + 'manufacturer': manufacturer.toJson(), + 'model': model.toJson(), + 'assetNDModel': assetNDModel.toJson(), + 'site': site.toJson(), + 'building': building.toJson(), + 'floor': floor.toJson(), + 'department': department.toJson(), + 'room': room, + 'assetType': assetType.toJson(), + 'assignedEmployee': assignedEmployee.toJson(), + 'lastActivityStatus': lastActivityStatus, + 'status': status.toJson(), + 'nextStep': nextStep.toJson(), + 'assetVerificationType': assetVerificationType, + 'workOrderContactPerson': workOrderContactPerson.map((i) => i.toJson()).toList(), + 'equipmentStatus': equipmentStatus.toJson(), + 'priority': priority.toJson(), + 'requestedThrough': requestedThrough.toJson(), + 'typeofRequest': typeofRequest.toJson(), + 'loanAvailablity': loanAvailablity, + 'assetLoan': assetLoan, + 'safety': safety, + 'problemDescription': problemDescription.toJson(), + 'comments': comments, + 'voiceNote': voiceNote, + 'workOrderAttachments': workOrderAttachments, + 'returnToService': returnToService, + 'serviceType': serviceType, + 'failureReasone': failureReasone, + 'solution': solution, + 'totalWorkingHours': totalWorkingHours, + 'workOrderHistory': workOrderHistory.map((i) => i.toJson()).toList(), + 'activityMaintenances': activityMaintenances, + 'activitySpareParts': activitySpareParts, + 'activityAssetToBeRetireds': activityAssetToBeRetireds, + }; + } + +} + +class WorkOrderCreatedBy { + String id; + String userName; + + WorkOrderCreatedBy({this.id, this.userName}); + + factory WorkOrderCreatedBy.fromJson(Map json) { + return WorkOrderCreatedBy( + id: json['id'], + userName: json['userName'], + ); + } + Map toJson() { + return { + 'id': id, + 'userName': userName, + }; + } +} + +class Asset { + int id; + String assetNumber; + + Asset({this.id, this.assetNumber}); + + factory Asset.fromJson(Map json) { + return Asset( + id: json['id'], + assetNumber: json['assetNumber'], + ); + } + Map toJson() { + return { + 'id': id, + 'assetNumber': assetNumber, + }; + } +} + +class AssetGroup { + int id; + String name; + + AssetGroup({this.id, this.name}); + + factory AssetGroup.fromJson(Map json) { + return AssetGroup( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } + +} + +class Manufacturer { + int id; + String name; + + Manufacturer({this.id, this.name}); + + factory Manufacturer.fromJson(Map json) { + return Manufacturer( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class Model { + int id; + String name; + + Model({this.id, this.name}); + + factory Model.fromJson(Map json) { + return Model( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class AssetNDModel { + int id; + String name; + + AssetNDModel({this.id, this.name}); + + factory AssetNDModel.fromJson(Map json) { + return AssetNDModel( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class Site { + int id; + String siteName; + + Site({this.id, this.siteName}); + + factory Site.fromJson(Map json) { + return Site( + id: json['id'], + siteName: json['siteName'], + ); + } + Map toJson() { + return { + 'id': id, + 'siteName': siteName, + }; + } +} + +class Building { + int id; + String name; + int value; + + Building({this.id, this.name, this.value}); + + factory Building.fromJson(Map json) { + return Building( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class Floor { + int id; + String name; + int value; + + Floor({this.id, this.name, this.value}); + + factory Floor.fromJson(Map json) { + return Floor( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class Department { + int id; + String name; + + Department({this.id, this.name}); + + factory Department.fromJson(Map json) { + return Department( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class AssetType { + int id; + String name; + int value; + + AssetType({this.id, this.name, this.value}); + + factory AssetType.fromJson(Map json) { + return AssetType( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class AssignedEmployee { + String id; + String userName; + + AssignedEmployee({this.id, this.userName}); + + factory AssignedEmployee.fromJson(Map json) { + return AssignedEmployee( + id: json['id'], + userName: json['userName'], + ); + } + Map toJson() { + return { + 'id': id, + 'userName': userName, + }; + } +} + +class Status { + int id; + String name; + int value; + + Status({this.id, this.name, this.value}); + + factory Status.fromJson(Map json) { + return Status( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class NextStep { + int id; + String name; + int value; + + NextStep({this.id, this.name, this.value}); + + factory NextStep.fromJson(Map json) { + return NextStep( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class WorkOrderContactPerson { + int id; + String name; + String employeeId; + String position; + String extension; + String email; + String mobilePhone; + ContactUser contactUser; + + WorkOrderContactPerson({ + this.id, + this.name, + this.employeeId, + this.position, + this.extension, + this.email, + this.mobilePhone, + this.contactUser, + }); + + factory WorkOrderContactPerson.fromJson(Map json) { + return WorkOrderContactPerson( + id: json['id'], + name: json['name'], + employeeId: json['employeeId'], + position: json['position'], + extension: json['extension'], + email: json['email'], + mobilePhone: json['mobilePhone'], + contactUser: ContactUser.fromJson(json['contactUser']), + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + 'employeeId': employeeId, + 'position': position, + 'extension': extension, + 'email': email, + 'mobilePhone': mobilePhone, + 'contactUser': contactUser.toJson(), + }; + } +} + +class ContactUser { + String id; + String userName; + + ContactUser({ + this.id, + this.userName, + }); + + factory ContactUser.fromJson(Map json) { + return ContactUser( + id: json['id'], + userName: json['userName'], + ); + } + + Map toJson() { + return { + 'id': id, + 'userName': userName, + }; + } +} + + +class EquipmentStatus { + int id; + String name; + int value; + + EquipmentStatus({this.id, this.name, this.value}); + + factory EquipmentStatus.fromJson(Map json) { + return EquipmentStatus( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class Priority { + int id; + String name; + int value; + + Priority({this.id, this.name, this.value}); + + factory Priority.fromJson(Map json) { + return Priority( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class RequestedThrough { + int id; + String name; + + RequestedThrough({this.id, this.name}); + + factory RequestedThrough.fromJson(Map json) { + return RequestedThrough( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class TypeOfRequest { + int id; + String name; + + TypeOfRequest({this.id, this.name}); + + factory TypeOfRequest.fromJson(Map json) { + return TypeOfRequest( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class ProblemDescription { + int id; + String name; + + ProblemDescription({this.id, this.name}); + + factory ProblemDescription.fromJson(Map json) { + return ProblemDescription( + id: json['id'], + name: json['name'], + ); + } + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } +} + +class WorkOrderHistory { + int id; + WorkOrderStatus workOrderStatus; + dynamic activityStatus; // Since activityStatus is null, it's dynamic + String date; + HistoryUser user; + Step step; + dynamic fixRemotelyStartTime; // Since it's null, it's dynamic + dynamic fixRemotelyEndTime; // Since it's null, it's dynamic + dynamic fixRemotelyWorkingHours; // Since it's null, it's dynamic + String comments; + dynamic needAVisitDateTime; // Since it's null, it's dynamic + + WorkOrderHistory({ + this.id, + this.workOrderStatus, + this.activityStatus, + this.date, + this.user, + this.step, + this.fixRemotelyStartTime, + this.fixRemotelyEndTime, + this.fixRemotelyWorkingHours, + this.comments, + this.needAVisitDateTime, + }); + + factory WorkOrderHistory.fromJson(Map json) { + return WorkOrderHistory( + id: json['id'], + workOrderStatus: WorkOrderStatus.fromJson(json['workorderStatus']), + activityStatus: json['activityStatus'], + date: json['date'], + user: HistoryUser.fromJson(json['user']), + step: Step.fromJson(json['step']), + fixRemotelyStartTime: json['fixRemotlyStartTime'], + fixRemotelyEndTime: json['fixRemotlyEndTime'], + fixRemotelyWorkingHours: json['fixRemotlyWorkingHours'], + comments: json['comments'] ?? "", + needAVisitDateTime: json['needAVisitDateTime'], + ); + } + + Map toJson() { + return { + 'id': id, + 'workorderStatus': workOrderStatus.toJson(), + 'activityStatus': activityStatus, + 'date': date, + 'user': user.toJson(), + 'step': step.toJson(), + 'fixRemotelyStartTime': fixRemotelyStartTime, + 'fixRemotelyEndTime': fixRemotelyEndTime, + 'fixRemotelyWorkingHours': fixRemotelyWorkingHours, + 'comments': comments, + 'needAVisitDateTime': needAVisitDateTime, + }; + } +} + +class WorkOrderStatus { + int id; + String name; + int value; + + WorkOrderStatus({ + this.id, + this.name, + this.value, + }); + + factory WorkOrderStatus.fromJson(Map json) { + return WorkOrderStatus( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + +class HistoryUser { + String id; + String userName; + + HistoryUser({ + this.id, + this.userName, + }); + + factory HistoryUser.fromJson(Map json) { + return HistoryUser( + id: json['id'], + userName: json['userName'], + ); + } + + Map toJson() { + return { + 'id': id, + 'userName': userName, + }; + } +} + +class Step { + int id; + String name; + int value; + + Step({ + this.id, + this.name, + this.value, + }); + + factory Step.fromJson(Map json) { + return Step( + id: json['id'], + name: json['name'], + value: json['value'], + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + 'value': value, + }; + } +} + diff --git a/lib/new_views/common_widgets/app_dashed_button.dart b/lib/new_views/common_widgets/app_dashed_button.dart index 7d2a0b02..757d9d77 100644 --- a/lib/new_views/common_widgets/app_dashed_button.dart +++ b/lib/new_views/common_widgets/app_dashed_button.dart @@ -10,23 +10,35 @@ import '../app_style/app_color.dart'; class AppDashedButton extends StatelessWidget { final String title; final VoidCallback onPressed; + double height; + Widget icon; - const AppDashedButton({@required this.title, @required this.onPressed, Key key}) : super(key: key); + AppDashedButton({@required this.title, @required this.onPressed, Key key,this.height,this.icon}) : super(key: key); @override Widget build(BuildContext context) { return Container( width: double.infinity, + height: height, padding: EdgeInsets.symmetric(horizontal: 2.toScreenWidth), decoration: BoxDecoration(color: AppColor.background(context), borderRadius: BorderRadius.circular(10)), child: DottedBorder( - strokeWidth: 2, + strokeWidth: 1, padding: EdgeInsets.symmetric(vertical: 16.toScreenHeight, horizontal: 16.toScreenWidth), - color: context.isDark ? AppColor.primary40 : AppColor.primary10, + color: context.isDark ? AppColor.primary40 : AppColor.black20, dashPattern: const [4, 3], radius: const Radius.circular(10), borderType: BorderType.RRect, - child: title.heading6(context).custom(color: context.isDark ? AppColor.primary40 : AppColor.primary10).center, + child:icon!=null? + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + 7.width, + title.heading6(context).custom(color: context.isDark ? AppColor.primary40 : AppColor.black10).center, + ], + ): + title.heading6(context).custom(color: context.isDark ? AppColor.primary40 : AppColor.primary10).center, ), ).onPress(onPressed); } diff --git a/lib/new_views/pages/land_page/dashboard_page.dart b/lib/new_views/pages/land_page/dashboard_page.dart index eb1b9127..58d96b05 100644 --- a/lib/new_views/pages/land_page/dashboard_page.dart +++ b/lib/new_views/pages/land_page/dashboard_page.dart @@ -14,9 +14,9 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/user.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/progress_fragment.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/recent_activites_fragment.dart'; -import 'package:test_sa/new_views/pages/land_page/dashboard_fragments/requests_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/recent_activites_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart'; import 'package:test_sa/views/pages/user/notifications/notifications_page.dart'; class DashboardPage extends StatefulWidget { diff --git a/lib/new_views/pages/land_page/requests_list_page.dart b/lib/new_views/pages/land_page/requests_list_page.dart index 551583ea..292eb7dd 100644 --- a/lib/new_views/pages/land_page/requests_list_page.dart +++ b/lib/new_views/pages/land_page/requests_list_page.dart @@ -10,7 +10,7 @@ import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/models/enums/user_types.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 'package:test_sa/new_views/pages/land_page/dashboard_fragments/request_category_list.dart'; +import 'package:test_sa/dashboard_latest/widgets/request_category_list.dart'; import 'package:test_sa/new_views/pages/land_page/requests/request_paginated_listview.dart'; import 'package:test_sa/new_views/pages/land_page/widgets/request_item_view_list.dart'; import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; diff --git a/lib/providers/work_order/reason_provider.dart b/lib/providers/work_order/reason_provider.dart index bd53bf99..e99c91e6 100644 --- a/lib/providers/work_order/reason_provider.dart +++ b/lib/providers/work_order/reason_provider.dart @@ -18,6 +18,7 @@ class ReasonProvider extends LoadingListNotifier { loading = true; notifyListeners(); try { + print('get reson data called...$serviceRequestId'); Response response = await ApiManager.instance.get(URLs.getServiceReportReasons+"&serviceRequestId=$serviceRequestId"); stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { diff --git a/lib/service_request_latest/request_detail_provider.dart b/lib/service_request_latest/request_detail_provider.dart new file mode 100644 index 00000000..216efe9a --- /dev/null +++ b/lib/service_request_latest/request_detail_provider.dart @@ -0,0 +1,450 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/models/helper_data_models/workorder/fix_remotely_model.dart'; +import 'package:test_sa/models/helper_data_models/workorder/need_visit_model.dart'; +import 'package:test_sa/models/helper_data_models/workorder/nurse_action_model.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/models/new_models/workOrderDetail.dart'; +import 'package:test_sa/models/service_request/service_report.dart'; +import 'package:test_sa/models/service_request/service_request.dart'; +import 'package:test_sa/models/service_request/spare_parts.dart'; +import 'package:test_sa/models/service_request/supp_engineer_work_orders.dart'; +import 'package:test_sa/models/service_request/supplier_engineer_model.dart'; +import '../../../models/service_request/wo_call_request.dart'; +import '../../../models/user.dart'; +import '../../../new_views/common_widgets/app_lazy_loading.dart'; + +class RequestDetailProvider extends ChangeNotifier { + final pageItemNumber = 10; + void reset() { + nextPage = true; + stateCode = null; + } + + + bool _isLoading = false; + + bool get isLoading => _isLoading; + + set isLoading(bool value) { + _isLoading = value; + notifyListeners(); + } + + WorkOrderDetail _currentWorkOrder; + FixRemotely _fixRemotelyModel; + NeedVisit _needVisitModel; + NurseActionModel _nurseActionModel; + + + NurseActionModel get nurseActionModel => _nurseActionModel; + + set nurseActionModel(NurseActionModel value) { + _nurseActionModel = value; + notifyListeners(); + } + + NeedVisit get needVisitModel => _needVisitModel; + + set needVisitModel(NeedVisit value) { + _needVisitModel = value; + notifyListeners(); + } + + FixRemotely get fixRemotelyModel => _fixRemotelyModel; + + set fixRemotelyModel(FixRemotely value) { + _fixRemotelyModel = value; + notifyListeners(); + } + + WorkOrderDetail get currentWorkOrder => _currentWorkOrder; + + set currentWorkOrder(WorkOrderDetail value) { + _currentWorkOrder = value; + notifyListeners(); + } + + int stateCode; +// true if there is next page in product list and false if not + bool nextPage = true; + TimeOfDay _selectedTime ; + + TimeOfDay get selectedTime => _selectedTime; + + set selectedTime(TimeOfDay value) { + _selectedTime = value; + notifyListeners(); + } // list of user requests + + SparePartsWorkOrders _initialSelectedSparePart = SparePartsWorkOrders(); + int _selectedListIndex = 0; + + int get selectedListIndex => _selectedListIndex; + + set selectedListIndex(int value) { + _selectedListIndex = value; + notifyListeners(); + } + //getWorkOrderById...... + Future getWorkOrderById({@required String id}) async { + try { + isLoading = true; + final response = await ApiManager.instance.get(URLs.getWorkOrderByIdUrl+"?workOrderId=$id"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + currentWorkOrder = WorkOrderDetail.fromJson(json.decode(response.body)); + notifyListeners(); + isLoading = false; + return currentWorkOrder; + } + isLoading= false; + notifyListeners(); + return null; + } + catch (e) { + log("getSubWorkOrderDetails [error] : $e"); + isLoading = false; + notifyListeners(); + return null; + } + } + //engineerAcceptWorkOrder...... + Future engineerAcceptWorkOrder({@required String id}) async { + try { + final body = { + "workOrderId": id, + }; + isLoading = true; + final response = await ApiManager.instance.post(URLs.engineerAcceptUrl,body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of Engineer accept workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //engineerRejectWorkOrder...... + Future engineerRejectWorkOrder({@required String id,String feedBack}) async { + try { + final body = { + "workOrderId": id, + "feedback": feedBack, + }; + isLoading = true; + final response = await ApiManager.instance.post(URLs.engineerRejectUrl,body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of Engineer reject workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //engineerFixRemotelyWorkOrder...... + Future engineerFixRemotely() async { + try { + isLoading = true; + final response = await ApiManager.instance.post(URLs.engineerFixRemotlyUrl,body: fixRemotelyModel.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of Engineer fixremotely workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //engineerNeedAVisitWorkOrder...... + Future engineerNeedVisit() async { + try { + isLoading = true; + final response = await ApiManager.instance.post(URLs.engineerNeedVisitUrl,body: needVisitModel.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of Engineer fixremotely workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //assignEngineerToWorkOrder...... + Future assignEngineerWorkOrder({@required String workOrderId,@required String engineerId}) async { + try { + final body = { + "workOrderId": workOrderId, + "assignedEngineerId": engineerId, + }; + isLoading = true; + final response = await ApiManager.instance.post(URLs.assignEngineerToWorkOrderUrl,body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of Engineer assignEngineer workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //Nurse confirm reopen + Future nurseReopen() async { + try { + isLoading = true; + final response = await ApiManager.instance.post(URLs.nurseConfirmReopenUrl,body: nurseActionModel.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of nurse confirmreopen workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + //Nurse confirm close + Future nurseClose() async { + try { + isLoading = true; + final response = await ApiManager.instance.post(URLs.nurseConfirmCloseUrl,body: nurseActionModel.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + CommonResponseModel commonResponseModel = CommonResponseModel.fromJson(json.decode(response.body)); + print('response of nurse confirmreopen workorder ${commonResponseModel.toJson()}'); + notifyListeners(); + isLoading = false; + return commonResponseModel; + } + isLoading= false; + notifyListeners(); + return CommonResponseModel(); + } + catch (e) { + log("engineer accept [error] : $e"); + isLoading = false; + notifyListeners(); + return CommonResponseModel(); + } + } + + + Future updateRequest({@required User user, @required ServiceRequest request}) async { + isLoading = true; + notifyListeners(); + Map serviceRequest = await getWorkOrderById(id: request.id) ?? ""; + final List callSiteContacts = (serviceRequest['callSiteContactPerson'] as List); + final Map callSiteContactPerson = callSiteContacts.isEmpty ? {} : callSiteContacts[0]; + Response response; + try { + if ((request.audio ?? "").isNotEmpty) { + request.audio = request.audio.substring(request.audio.indexOf("=") + 1, request.audio.length); + } + } catch (ex) {} + + var body = { + "id": request.id, + "callNo": serviceRequest['callNo'], + "callCreatedBy": serviceRequest['callCreatedBy'], + "requestedDate": request.date ?? "", + "requestedTime": request.date ?? "", + "priority": request.priority?.toJson(), + "defectType": request.defectType?.toJson(), + "typeofRequest": request.type?.toJson(), + "requestedThrough": request.requestedThrough?.toJson(), + "voiceNote": request.audio, + "assets": request.deviceId == null ? [] : [request.deviceId], + "attachmentsCallRequest": (request.devicePhotos?.isNotEmpty ?? false) ? request.devicePhotos?.map((e) => {"name": e.getFileName})?.toList() : [], + "assignedEmployee": { + "id": request.engineerId, + "name": request.engineerName, + }, + "callSiteContactPerson": [ + { + "id": callSiteContactPerson['id'] ?? 0, + "employeeCode": callSiteContactPerson['employeeCode'], + "name": callSiteContactPerson['name'] ?? user.userName, + "telephone": callSiteContactPerson['telephone'] ?? user.phoneNumber, + "job": callSiteContactPerson['job'], + "email": callSiteContactPerson['email'] ?? user.email, + "land": callSiteContactPerson['land'], + "contactUserId": user.userID, + }, + ], + "callComments": request.callComments, + "noofFollowup": 0, + "status": request.statusId == null + ? null + : { + "id": request.statusId, + "name": request.statusLabel, + "value": request.statusValue, + }, + "callLastSituation": null, + "firstAction": request.firstAction?.toJson(), + "loanAvailablity": request.loanAvailability?.toJson(), + "comments": request.comments, + "firstActionDate": request.visitDate, + "visitDate": request.visitDate, + "startDate": request.startDate, + "endDate": request.endDate, + "workingHours": request.workingHours, + "callReview": null, + "reviewComment": null, + //"reviewComment": request.reviewComment, + }; + try { + response = await ApiManager.instance.put( + URLs.updateRequestDate, + body: body, + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // request.engineerName = employee.name; + notifyListeners(); + } + isLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isLoading = false; + notifyListeners(); + return -1; + } + } + + Future createServiceReport(BuildContext context, {@required ServiceReport report, @required User user, @required ServiceRequest request}) async { + Response response; + try { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + final body = report.toJson(); + final callRequest = CallRequest(id: num.tryParse(request?.id ?? "")).toJson(); + final contactPerson = [ + { + "id": 0, + // "employeeCode": "", + "name": user.userName, + "telephone": user.phoneNumber, + // "job": "", + "email": user.email, + // "land": "", + "contactUserId": user.userID, + } + ]; + body.update("contactPersonWorkOrders", (value) => contactPerson, ifAbsent: () => contactPerson); + body.update("callRequest", (value) => callRequest, ifAbsent: () => callRequest); + response = await ApiManager.instance.post(URLs.createServiceReport, body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + reset(); + notifyListeners(); + Fluttertoast.showToast(msg: context.translation.successfulRequestMessage); + Navigator.of(context).pop(); + } else { + Fluttertoast.showToast(msg: "${context.translation.failedToCompleteRequest}: ${json.decode(response.body)['message']}"); + } + Navigator.pop(context); + return response.statusCode; + } catch (error) { + Navigator.pop(context); + print(error); + return -1; + } + } + + Future addSupplierEngineer(SupplierEngineer supplierEngineer) async { + isLoading = true; + notifyListeners(); + Response response; + SuppEngineerWorkOrders res; + try { + response = await ApiManager.instance.post(URLs.addSupplierEngineer, body: supplierEngineer.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + res = SuppEngineerWorkOrders.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return res; + } catch (error) { + isLoading = false; + notifyListeners(); + return res; + } + } + + SparePartsWorkOrders get initialSelectedSparePart => _initialSelectedSparePart; + + set initialSelectedSparePart(SparePartsWorkOrders value) { + _initialSelectedSparePart = value; + notifyListeners(); + } +} diff --git a/lib/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart b/lib/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart index 05230e79..599854c6 100644 --- a/lib/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart +++ b/lib/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart @@ -15,7 +15,7 @@ import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/models/timer_model.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; import 'package:test_sa/providers/service_request_providers/first_action_provider.dart'; -import 'package:test_sa/service_request_latest/views/components/spare_part_request.dart'; +import 'package:test_sa/service_request_latest/views/forms/spare_part/spare_part_request.dart'; import 'package:test_sa/service_request_latest/views/components/verify_arrival_view.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/date_and_time/time_picker.dart'; @@ -391,7 +391,6 @@ class ServiceRequestBottomSheet { alignment: AlignmentDirectional.centerStart, child: context.translation.setVisitDate.bottomSheetHeadingTextStyle(context), ), - 15.height, ADatePicker( label: context.translation.visitDate, @@ -427,8 +426,6 @@ class ServiceRequestBottomSheet { } }, ), - // ], - if (serviceRequestProvider.currentSelectedRequest.firstAction?.id == 404 && Provider.of(context, listen: false).assetGroup.id == 1) ...[ 8.height, Row( diff --git a/lib/service_request_latest/views/components/request_detail_view.dart b/lib/service_request_latest/views/components/request_detail_view.dart index f85551e3..71ef3ae9 100644 --- a/lib/service_request_latest/views/components/request_detail_view.dart +++ b/lib/service_request_latest/views/components/request_detail_view.dart @@ -15,6 +15,9 @@ 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/default_app_bar.dart'; import 'package:test_sa/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/components/attachments_view.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/work_order_form_view.dart'; import 'package:test_sa/views/pages/user/requests/comments_bottom_sheet.dart'; import 'package:test_sa/views/pages/user/requests/update_service_request_page.dart'; import 'package:test_sa/views/pages/user/requests/work_order/work_orders_list_page.dart'; @@ -47,6 +50,7 @@ class _RequestDetailViewState extends State { Provider.of(context, listen: false).reset(); ServiceRequestsProvider serviceRequestsProvider = Provider.of(context, listen: false); serviceRequestsProvider.currentSelectedRequest = await serviceRequestsProvider.getServiceRequestObjectById(requestId: requestId); + print('request id i got is ${serviceRequestsProvider.currentSelectedRequest.id}'); // setState(() {}); }); } @@ -117,7 +121,9 @@ class _RequestDetailViewState extends State { textColor: AppColor.red30, showBorder: true, onPressed: () async { - ServiceRequestBottomSheet.rejectRequestBottomSheet(context: context); + // ServiceRequestBottomSheet.rejectRequestBottomSheet(context: context); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => const WorkOrderFormView())); + // bool shouldReloadData = (await showModalBottomSheet( // context: context, // useSafeArea: true, diff --git a/lib/service_request_latest/views/components/spare_part_request.dart b/lib/service_request_latest/views/forms/spare_part/spare_part_request.dart similarity index 95% rename from lib/service_request_latest/views/components/spare_part_request.dart rename to lib/service_request_latest/views/forms/spare_part/spare_part_request.dart index c2e4cd2e..47c22606 100644 --- a/lib/service_request_latest/views/components/spare_part_request.dart +++ b/lib/service_request_latest/views/forms/spare_part/spare_part_request.dart @@ -20,13 +20,13 @@ import 'package:test_sa/providers/work_order/reason_provider.dart'; import 'package:test_sa/service_request_latest/views/components/bottom_sheets/service_request_bottomsheet.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; -import '../../../../../controllers/providers/api/status_drop_down/report/service_report_last_calls_provider.dart'; -import '../../../../../controllers/providers/api/status_drop_down/report/service_types_provider.dart'; -import '../../../../../models/lookup.dart'; -import '../../../../../models/service_request/spare_parts.dart'; -import '../../../../../new_views/common_widgets/app_text_form_field.dart'; -import '../../../../../new_views/common_widgets/default_app_bar.dart'; -import '../../../controllers/validator/validator.dart'; +import '../../../../../../controllers/providers/api/status_drop_down/report/service_report_last_calls_provider.dart'; +import '../../../../../../controllers/providers/api/status_drop_down/report/service_types_provider.dart'; +import '../../../../../../models/lookup.dart'; +import '../../../../../../models/service_request/spare_parts.dart'; +import '../../../../../../new_views/common_widgets/app_text_form_field.dart'; +import '../../../../../../new_views/common_widgets/default_app_bar.dart'; +import '../../../../controllers/validator/validator.dart'; class SparePartRequest extends StatefulWidget { static const String id = "/spare-part-request"; diff --git a/lib/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart b/lib/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart new file mode 100644 index 00000000..1b881fa1 --- /dev/null +++ b/lib/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart @@ -0,0 +1,263 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/parts_provider.dart'; +import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; +import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_fault_description_provider.dart'; +import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_last_calls_provider.dart'; +import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_types_provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/controllers/validator/validator.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/service_request/search_work_order.dart'; +import 'package:test_sa/models/service_request/service_report.dart'; +import 'package:test_sa/models/service_request/spare_parts.dart'; +import 'package:test_sa/models/service_request/supp_engineer_work_orders.dart'; +import 'package:test_sa/models/service_request/supplier_details.dart'; +import 'package:test_sa/models/service_request/wo_call_request.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/service_request_providers/equipment_status_provider.dart'; +import 'package:test_sa/providers/work_order/reason_provider.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; + + +class AssetConditionView extends StatefulWidget { + static const id = "/CreateSubWorkOrder"; + final SearchWorkOrder workOrder; + + const AssetConditionView({this.workOrder, Key key}) : super(key: key); + + @override + State createState() => _AssetConditionViewState(); +} + +class _AssetConditionViewState extends State { + final GlobalKey _formKey = GlobalKey(); + SearchWorkOrder _subWorkOrders; + ServiceReport _serviceReport; + PartsProvider _partsProvider; + List _spareParts = []; + bool _isLoading = false; + bool _showVendorFields = false; + SuppEngineerWorkOrders engineer; + SupplierDetails initialSupplier; + final TextEditingController _workPreformedController = TextEditingController(); + final TextEditingController _partQtyController = TextEditingController(); + + @override + void initState() { + _subWorkOrders = SearchWorkOrder( + assignedEmployee: widget?.workOrder?.callRequest?.assignedEmployee, + callRequest: CallRequest(id: widget?.workOrder?.callRequest?.id), + currentSituation: null, + supplier: null, + // parentWOId: widget.workOrder.id, + ); + getData(); + _serviceReport = ServiceReport(); + // _isLoading = true; + super.initState(); + // if (context.mounted) { + // Provider.of(context, listen: false).reset(); + // Provider.of(context, listen: false).reset(); + // Provider.of(context, listen: false).serviceRequestId = widget.workOrder.callRequest.id.toString(); + // } + } + + ServiceStatusProvider assetTypesProvider; + CallRequest _callRequestForWorkOrder; + + void getData() async { + ReasonProvider reasonProvider = Provider.of(context,listen: false); + ServiceRequestsProvider serviceRequestsProvider = Provider.of(context,listen: false); + _spareParts = await Provider.of(context,listen: false).getPartsList(assetId: serviceRequestsProvider.currentSelectedRequest.deviceId); + + // _serviceReport = serviceRequestsProvider.currentSelectedRequest; + // Provider.of(context).getDate(); + // Provider.of(context).getDate(); + reasonProvider.serviceRequestId = serviceRequestsProvider.currentSelectedRequest.id; + reasonProvider.getDate(); + // Provider.of(context).getDate(); + // Provider.of(context).getDate(); + } + Future getAssetType() async { + Provider.of(context, listen: false).reset(); + final serviceRequestProvider = Provider.of(context); + Provider.of(context, listen: false).reset(); + assetTypesProvider = Provider.of(context, listen: false); + _callRequestForWorkOrder = await serviceRequestProvider.getCallRequestForWorkOrder(callId: widget.workOrder?.callRequest?.id?.toString()); + if (_subWorkOrders?.parentWOId != null) { + final subWoDetails = await serviceRequestProvider.getSubWorkOrderDetails(parentId: _subWorkOrders?.parentWOId.toString()); + _subWorkOrders.copyDetails(subWoDetails); + _serviceReport.equipmentStatus = subWoDetails.equipmentStatus; + _serviceReport.reason = subWoDetails.reason; + initialSupplier = subWoDetails.supplier; + _serviceReport.faultDescription = subWoDetails.faultDescription; + _subWorkOrders.visitDate = subWoDetails.visitDate; + _subWorkOrders.sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders; + } + await assetTypesProvider.getTypes(); + _subWorkOrders?.assetType = assetTypesProvider.statuses?.firstWhere( + (element) => element.value == _callRequestForWorkOrder?.assetType, + orElse: () => null, + ); + if (checkVendorFieldsVisibility(_subWorkOrders.calllastSituation)) { + if (_subWorkOrders.suppEngineerWorkOrders?.isNotEmpty ?? false) { + engineer = _subWorkOrders.suppEngineerWorkOrders?.last; + engineer?.id = engineer?.supplierContactId; + } + _subWorkOrders.supplier ??= SupplierDetails(id: _subWorkOrders?.supplier?.id); + } + _spareParts = await _partsProvider.getPartsList(assetId: widget.workOrder?.callRequest?.asset?.id); + setState(() { + _isLoading = false; + }); + } + + Asset loanAvailabilityAsset; + + @override + void dispose() { + _workPreformedController?.dispose(); + _partQtyController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final user = Provider.of(context).user; + // final isCurrentUserIsAssistantEmp = (user.userID != widget.workOrder.assignedEmployee?.id); + // if (isCurrentUserIsAssistantEmp) { + // // _subWorkOrders.assistantEmployees = [widget.workOrder.assistantEmployees?.first?.copyWith(id: 0)]; + // _subWorkOrders.assistantEmployees = [AssistantEmployees(id: 0, user: AssignedEmployee(id: user.userID, name: user.username))]; + // } else {} + // if (_callRequestForWorkOrder == null) { + // _partsProvider = Provider.of(context); + // // getAssetType(); + // } + + bool disablePart = _subWorkOrders.calllastSituation?.value == 12; + + return Consumer( + builder: (context, serviceRequestProvider,child) { + return SafeArea( + child: _isLoading + ? const ALoading() + : Container( + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth), + child: Column( + children: [ + SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SingleItemDropDownMenu( + context: context, + title: context.translation.reason, + initialValue: _subWorkOrders.reason, + onSelect: (value) { + if (value != null) { + _subWorkOrders.reason = value; + } + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.equipmentStatus, + initialValue: _subWorkOrders.equipmentStatus, + onSelect: (value) { + if (value != null) { + _subWorkOrders.equipmentStatus = value; + } + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.partNo, + staticData: _spareParts, + showShadow: true, + initialValue: serviceRequestProvider.initialSelectedSparePart.sparePart, + backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.white10, + onSelect: (part) { + serviceRequestProvider.initialSelectedSparePart = SparePartsWorkOrders(id: 0, sparePart: part, qty: 0); + }, + ), + // AppTextFormField( + // controller: _partQtyController, + // labelText: context.translation.quantity, + // textInputType: TextInputType.number, + // contentPadding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 20.toScreenHeight), + // showWithoutDecoration: true, + // backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90, + // enable: serviceRequestProvider.initialSelectedSparePart != null && serviceRequestProvider.initialSelectedSparePart.sparePart?.id != null, + // validator: (value) => value == null || value.isEmpty + // ? context.translation.requiredField + // : Validator.isNumeric(value) + // ? null + // : context.translation.onlyNumbers, + // onSaved: (text) { + // serviceRequestProvider.initialSelectedSparePart.qty = num.tryParse(text ?? ""); + // }, + // ), + 15.height, + ], + ), + ),),]), + )); + } + ); + + // ), + // ); + } + + bool checkVendorFieldsVisibility(Lookup callsLastSituation) { + bool result = (initialSupplier?.suppliername?.isNotEmpty ?? false) || + (callsLastSituation?.name?.toLowerCase()?.contains("under repair-vendor") ?? false) || + (callsLastSituation?.name?.toLowerCase()?.contains("waiting for vendor") ?? false); + setState(() { + _showVendorFields = result; + }); + return result; + } + + Future validate() async { + if (_subWorkOrders.reason == null) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.reason}"); + return false; + } else if (_subWorkOrders.equipmentStatus == null) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.equipmentStatus}"); + return false; + } else if (_subWorkOrders.calllastSituation == null) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.callLastSituation}"); + return false; + } else if (_showVendorFields && _subWorkOrders.suppEngineerWorkOrders == null) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.supplierEngineer}"); + return false; + } else if (_showVendorFields && (_subWorkOrders.supplier.suppliername == null || _subWorkOrders.supplier.suppliername.isEmpty)) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.supplier}"); + return false; + } else if (_subWorkOrders.calllastSituation.value == 12 && (_subWorkOrders.sparePartsWorkOrders == null || _subWorkOrders.sparePartsWorkOrders.isEmpty)) { + Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.partNo}"); + return false; + } else if (_subWorkOrders?.timer?.startAt == null) { + await Fluttertoast.showToast(msg: "Working Hours Required"); + return false; + } else if (_subWorkOrders?.timer?.endAt == null) { + await Fluttertoast.showToast(msg: "Please Stop The Timer"); + return false; + } + return true; + } +} diff --git a/lib/service_request_latest/views/forms/work_order/components/attachments_view.dart b/lib/service_request_latest/views/forms/work_order/components/attachments_view.dart new file mode 100644 index 00000000..cb8a981a --- /dev/null +++ b/lib/service_request_latest/views/forms/work_order/components/attachments_view.dart @@ -0,0 +1,40 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/service_requests_provider.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/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; + +class AttachmentView extends StatelessWidget { + const AttachmentView({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + //TODO user the same form key everywhere.... + final GlobalKey _formKey = GlobalKey(); + final List _files = []; + return Consumer(builder: (context, serviceRequestProvider, child) { + serviceRequestProvider.currentSelectedRequest.visitDate = ''; + return Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + children: [ + MultiFilesPicker( + label: context.translation.attachImage, + buttonHeight: 108.toScreenHeight, + buttonIcon: 'image_icon'?.toSvgAsset(), + files: _files, + onlyImages: true, + ), + 16.height, + ], + ), + ), + ); + }); + } +} diff --git a/lib/service_request_latest/views/forms/work_order/components/time_duration_view.dart b/lib/service_request_latest/views/forms/work_order/components/time_duration_view.dart new file mode 100644 index 00000000..7f81c0cc --- /dev/null +++ b/lib/service_request_latest/views/forms/work_order/components/time_duration_view.dart @@ -0,0 +1,120 @@ + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/service_requests_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/widget_extensions.dart'; +import 'package:test_sa/models/timer_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/date_and_time/time_picker.dart'; +import 'package:test_sa/views/widgets/timer/app_timer.dart'; + +class TimeDurationView extends StatelessWidget { + const TimeDurationView({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + //TODO user the same form key everywhere.... + final GlobalKey _formKey = GlobalKey(); + return Consumer( + builder: (context, serviceRequestProvider,child) { + serviceRequestProvider.currentSelectedRequest.visitDate = ''; + return Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + children: [ + ADatePicker( + label: context.translation.visitDate, + hideShadow: true, + withIcon: false, + hint: context.translation.select, + height: 70.toScreenHeight, + date: DateTime.tryParse(serviceRequestProvider.currentSelectedRequest.visitDate ?? ""), + formatDateWithTime: true, + onDatePicker: (selectedDate) { + if (selectedDate != null) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + if (selectedDateTime != null) { + serviceRequestProvider.currentSelectedRequest.visitDate = selectedDateTime?.toIso8601String(); + } + } + }); + } + }, + ), + 8.height, + ATimePicker( + label: context.translation.startTime, + hint: context.translation.select, + withIcon: false, + withBorder: false, + height: 70.toScreenHeight, + time: serviceRequestProvider.selectedTime, + onTimePicker: (selectedTime) { + if (selectedTime != null) { + print('time selected $selectedTime'); + serviceRequestProvider.selectedTime = selectedTime; + } + }, + ), + 8.height, + ATimePicker( + label: context.translation.endTime, + hint: context.translation.select, + withIcon: false, + withBorder: false, + height: 70.toScreenHeight, + time: serviceRequestProvider.selectedTime, + onTimePicker: (selectedTime) { + if (selectedTime != null) { + print('time selected $selectedTime'); + serviceRequestProvider.selectedTime = selectedTime; + } + }, + ), + 8.height, + SizedBox( + width: double.infinity, + height: 70.toScreenHeight, + child: AppTimer( + label: context.translation.workingHours, + timer: TimerModel(), + decoration: BoxDecoration( + color: AppColor.background(context), + borderRadius: BorderRadius.circular(10), + // boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], + ), + // enabled: serviceRequestProvider.currentSelectedRequest.date == null, + enabled: true, + onChange: (timer) async { + print('timer i got is ${timer.toString()}'); + return true; + }, + ), + + ), + 16.height, + ], + ), + ), + ); + } + ); + } +} diff --git a/lib/service_request_latest/views/forms/work_order/work_order_form_view.dart b/lib/service_request_latest/views/forms/work_order/work_order_form_view.dart new file mode 100644 index 00000000..20f4a53a --- /dev/null +++ b/lib/service_request_latest/views/forms/work_order/work_order_form_view.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/common_widgets/stapper_widget/components/base_step.dart'; +import 'package:test_sa/common_widgets/stapper_widget/components/custom_line.dart'; +import 'package:test_sa/common_widgets/stapper_widget/custom_stepper.dart'; +import 'package:test_sa/controllers/providers/api/service_requests_provider.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/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/default_app_bar.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/components/asset_conditon_view.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/components/attachments_view.dart'; +import 'package:test_sa/service_request_latest/views/forms/work_order/components/time_duration_view.dart'; + +class WorkOrderFormView extends StatefulWidget { + + const WorkOrderFormView({Key key}) : super(key: key); + + @override + _WorkOrderFormViewState createState() => _WorkOrderFormViewState(); +} + +class _WorkOrderFormViewState extends State { + int currentStep = 0; + int activeStep = 0; + int reachedStep = 0; + int upperBound = 2; + final formKey = GlobalKey(); + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + Future getInitialData() async { + // ListingVm listingVm = Provider.of(context, listen: false); + // listingVm.listingPropertyType = widget.arguments['listingType']; + // if (widget.arguments['formType'] == AppConstants.addListing) { + // listingVm.landLordModel = LandLordModel(); + // listingVm.listing['propertyType'] = widget.arguments['listingType']; + // if (listingVm.listing['propertyType'] == AppConstants.sell) { + // listingVm.listingParameter = 'sellParameter'; + // listingVm.listing[listingVm.listingParameter] = + // SellParameter().toJson(); + // listingVm.listing[listingVm.listingParameter]?['ownerDocs'] = []; + // } + // if (listingVm.listing['propertyType'] == AppConstants.rent) { + // listingVm.listingParameter = 'rentParameter'; + // listingVm.listing[listingVm.listingParameter] = + // RentParameter().toJson(); + // listingVm.listing[listingVm.listingParameter]?['ownerDocs'] = []; + // } + // } else if (widget.arguments['formType'] == AppConstants.editListing) { + // if (listingVm.listing['propertyType'] == AppConstants.sell) { + // listingVm.listingParameter = 'sellParameter'; + // } + // if (listingVm.listing['propertyType'] == AppConstants.rent) { + // listingVm.listingParameter = 'rentParameter'; + // } + // if (widget.arguments['isDuplicate'] == true) { + // FormUtils.setDuplicateValues( + // listingVm: listingVm, listType: widget.arguments['listingType']); + // } + // listingVm.formLocationController.clear(); + // listingVm.formCommunityInitialValue = + // await FormUtils.setLocation(listingVm.listing, context); + // listingVm.formLocationController.text = + // listingVm.formCommunityInitialValue ?? ''; + // + // listingVm.formAvailabilityDate = + // listingVm.listing[listingVm.listingParameter]['availabilityDate'] ?? + // ''; + // listingVm.formBuildYearDate = + // listingVm.listing[listingVm.listingParameter]['buildYear'] ?? ''; + // + // listingVm.listing['photos'] ??= []; + // listingVm.listing[listingVm.listingParameter]['ownerDocs'] ??= []; + // } + // GeneralUtil.initilizePortalsSwitch(listingVm: listingVm); + // listingVm.getTenantExtent(); + // await listingVm.getRegion(); + // if (listingVm.listing[listingVm.listingParameter] + // ['propertyFinderLocation'] != + // null) { + // await listingVm.getPropertyFinderLocationList( + // searchQuery: listingVm.listing[listingVm.listingParameter] + // ['propertyFinderLocation']); + // } + // await listingVm.getWaterMarkImage(); + // await listingVm.getDeveloper(); + // await GeneralUtil.loadPortalsLocationsData(listingVm); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColor.neutral100, + appBar: DefaultAppBar(title: context.translation.createWorkOrder), + body: Column( + children: [ + 16.height, + CustomStepper( + activeStep: activeStep, + lineStyle: LineStyle( + activeLineColor: AppColor.primary10, + finishedLineColor: AppColor.primary10, + defaultLineColor: AppColor.white40, + lineThickness: 1, + lineLength: 93.toScreenWidth, + lineType: LineType.normal, + ), + internalPadding: 2.toScreenWidth, + steps: [ + StepModel(customStep: customStepWidget(index: 0, stepIndex: 1), customTitle: customStepLabel(index: 1, label: context.translation.timeAndDuration)), + StepModel(customStep: customStepWidget(index: 1, stepIndex: 2), customTitle: customStepLabel(index: 1, label: context.translation.assetsCondition)), + StepModel(customStep: customStepWidget(index: 2, stepIndex: 3), customTitle: customStepLabel(index: 1, label: context.translation.attachmentsAcknowledge)), + ], + onStepReached: (index) => setState(() => activeStep = index), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.symmetric(horizontal: 16.toScreenWidth, vertical: 25.toScreenHeight), + child: getStepWidget(), + )), + bottomContainerWidget(), + ], + ), + ); + } + + Widget bottomContainerWidget() { + return Container( + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth,vertical: 16.toScreenHeight), + color: AppColor.white10, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppFilledButton( + label: context.translation.cancel, + loading: false, + buttonColor: AppColor.white60, + textColor: AppColor.black10, + onPressed: () async { + Navigator.pop(context); + // await snapshot.updateRequest(user: userProvider.user, request: serviceRequestProvider.serviceRequest); + // Navigator.pop(context, true); + }, + ).expanded, + 12.width, + AppFilledButton( + label: context.translation.next, + buttonColor: AppColor.primary10, + loading: false, + onPressed: () { + _incrementActiveStep(); + //TODO move to next step.. + }, + ).expanded, + ], + )); + } + + void _incrementActiveStep() { + setState(() { + ++activeStep; + if (reachedStep < activeStep) { + reachedStep = activeStep; + } + }); + } + + Future _navigateNext(ServiceRequestsProvider serviceRequestsProvider) async { + if (activeStep < upperBound) { + switch (activeStep) { + case 0: + // await _navigateNextStep(listingVm); + break; + case 1: + break; + case 2: + _incrementActiveStep(); + break; + } + } + } + +// Your onPressed code + + Widget customStepWidget({@required int index, @required int stepIndex}) { + return CircleAvatar( + backgroundColor: activeStep >= index ? AppColor.primary10 : AppColor.background(context), + child: activeStep > index + ? Icon( + Icons.done, + color: AppColor.white10, + size: 26.toScreenHeight, + ) + : activeStep == index + ? Text( + '0$stepIndex', + style: const TextStyle(fontWeight: FontWeight.w500, color: AppColor.white10), + textAlign: TextAlign.center, + ) + : Text( + '0$stepIndex', + style: const TextStyle(color: AppColor.black20), + ), + ); + } + + Widget customStepLabel({@required String label, @required int index}) { + return Column( + children: [ + Text( + label, + textAlign: TextAlign.center, + style: AppTextStyles.bodyText2.copyWith(color: AppColor.black20), + ), + ], + ); + } + + /// Returns the previous button. + + Widget getStepWidget() { + switch (activeStep) { + case 0: + return const TimeDurationView(); + case 1: + return const AssetConditionView(); + case 2: + return const AttachmentView(); + default: + return const SizedBox(); + } + } +} diff --git a/lib/views/widgets/date_and_time/date_picker.dart b/lib/views/widgets/date_and_time/date_picker.dart index 8fa843b8..03b25ea2 100644 --- a/lib/views/widgets/date_and_time/date_picker.dart +++ b/lib/views/widgets/date_and_time/date_picker.dart @@ -14,7 +14,7 @@ class ADatePicker extends StatelessWidget { final bool hideShadow; final String hint; final Function(DateTime) onDatePicker; - final bool enable, withBorder; + final bool enable, withBorder, withIcon; final Color backgroundColor; final bool formatDateWithTime; final double height; @@ -25,7 +25,8 @@ class ADatePicker extends StatelessWidget { this.withBorder = true, this.height, this.backgroundColor, - this.hideShadow=false, + this.hideShadow = false, + this.withIcon = true, this.hint, this.date, this.formatDateWithTime = false, @@ -38,7 +39,7 @@ class ADatePicker extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height:height, + height: height, decoration: BoxDecoration( color: backgroundColor ?? (context.isDark && (enable == false) @@ -48,7 +49,7 @@ class ADatePicker extends StatelessWidget { : AppColor.background(context)), borderRadius: BorderRadius.circular(10), border: withBorder ? Border.all(width: 1, color: Theme.of(context).scaffoldBackgroundColor) : const Border(), - boxShadow: hideShadow?null: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], + boxShadow: hideShadow ? null : [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], ), padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), child: Row( @@ -56,20 +57,20 @@ class ADatePicker extends StatelessWidget { // enable // ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - label.tinyFont(context), - (date?.toIso8601String == null - ?hint?? context.translation.pickADate - : (formatDateWithTime ? date?.toIso8601String()?.toFirstActionFormat : (date?.toIso8601String()?.split("T")?.first ?? context.translation.pickADate))) - .bodyText(context) - .custom(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), - ], - ).expanded, - //: label.bodyText(context).paddingOnly(top: 8, bottom: 8), + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + label.tinyFont(context), + (date?.toIso8601String == null + ? hint ?? context.translation.pickADate + : (formatDateWithTime ? date?.toIso8601String()?.toFirstActionFormat : (date?.toIso8601String()?.split("T")?.first ?? context.translation.pickADate))) + .bodyText(context) + .custom(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ], + ).expanded, + //: label.bodyText(context).paddingOnly(top: 8, bottom: 8), enable ? 16.width : const Spacer(), - "calender".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null), + withIcon ? "calender".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null) : const SizedBox(), ], ), ).onPress(enable diff --git a/lib/views/widgets/date_and_time/time_picker.dart b/lib/views/widgets/date_and_time/time_picker.dart index 2752d455..93525999 100644 --- a/lib/views/widgets/date_and_time/time_picker.dart +++ b/lib/views/widgets/date_and_time/time_picker.dart @@ -11,7 +11,7 @@ class ATimePicker extends StatelessWidget { final String label; final String hint; final Function(TimeOfDay) onTimePicker; - final bool enable, withBorder; + final bool enable, withBorder, withIcon; final Color backgroundColor; final double height; @@ -19,6 +19,7 @@ class ATimePicker extends StatelessWidget { Key key, @required this.label, this.withBorder = true, + this.withIcon = true, this.height, this.backgroundColor, this.hint, @@ -31,14 +32,14 @@ class ATimePicker extends StatelessWidget { Widget build(BuildContext context) { print('initial time in picker is $time'); return Container( - height:height, + height: height, decoration: BoxDecoration( color: backgroundColor ?? (context.isDark && (enable == false) ? AppColor.neutral50 : (enable == false) - ? AppColor.neutral40 - : AppColor.background(context)), + ? AppColor.neutral40 + : AppColor.background(context)), borderRadius: BorderRadius.circular(10), border: withBorder ? Border.all(width: 1, color: Theme.of(context).scaffoldBackgroundColor) : const Border(), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], @@ -53,35 +54,33 @@ class ATimePicker extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ label.tinyFont(context), - (time == null - ?hint?? context.translation.pickTime - : (time.format(context)?? context.translation.pickADate)) + (time == null ? hint ?? context.translation.pickTime : (time.format(context) ?? context.translation.pickADate)) .bodyText(context) .custom(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ], ).expanded, //: label.bodyText(context).paddingOnly(top: 8, bottom: 8), enable ? 16.width : const Spacer(), - "calender".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null), + withIcon ? "calender".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null) : const SizedBox(), ], ), ).onPress(enable ? () async { - // Define the initial time - TimeOfDay initialTime = TimeOfDay.now(); + // Define the initial time + TimeOfDay initialTime = TimeOfDay.now(); - // Show the time picker with the initial time set - TimeOfDay pickedTime = await showTimePicker( - context: context, - initialTime: initialTime, - ); + // Show the time picker with the initial time set + TimeOfDay pickedTime = await showTimePicker( + context: context, + initialTime: initialTime, + ); - // Handle the selected time (if user didn't cancel) - if (pickedTime != null) { - if (onTimePicker != null) onTimePicker(pickedTime); - print("Selected time: ${pickedTime.format(context)}"); - } - } + // Handle the selected time (if user didn't cancel) + if (pickedTime != null) { + if (onTimePicker != null) onTimePicker(pickedTime); + print("Selected time: ${pickedTime.format(context)}"); + } + } : null); } } diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index abd00bb3..be2236ca 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -18,10 +18,13 @@ class MultiFilesPicker extends StatefulWidget { final bool error; final List files; final bool enabled, onlyImages; + double buttonHeight; + Widget buttonIcon; final Function(List) onChange; final bool showAsGrid; - const MultiFilesPicker({Key key, this.files, this.label, this.error = false, this.enabled = true, this.onlyImages = false, this.onChange,this.showAsGrid=false}) : super(key: key); + MultiFilesPicker({Key key, this.files, this.label, this.error = false, this.buttonHeight, this.buttonIcon, this.enabled = true, this.onlyImages = false, this.onChange, this.showAsGrid = false}) + : super(key: key); @override State createState() => _MultiFilesPickerState(); @@ -33,7 +36,15 @@ class _MultiFilesPickerState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppDashedButton(title: widget.label, onPressed: (widget.enabled == false) ? () {} : widget.showAsGrid ? showFileSourceSheet:onFilePicker), + AppDashedButton( + title: widget.label, + height: widget.buttonHeight, + icon: widget.buttonIcon, + onPressed: (widget.enabled == false) + ? () {} + : widget.showAsGrid + ? showFileSourceSheet + : onFilePicker), 16.height, if (widget.files?.isNotEmpty ?? false) Wrap( diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index f0fcb693..17f8d926 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -35,6 +35,7 @@ class MultiFilesPickerItem extends StatelessWidget { Container( margin: EdgeInsetsDirectional.only(top: 4.toScreenHeight, end: 6.toScreenWidth), decoration: BoxDecoration( + color: Colors.red, border: Border.all(width: 1, color: context.isDark ? AppColor.neutral30 : AppColor.neutral30), image: DecorationImage( image: isImage diff --git a/pubspec.yaml b/pubspec.yaml index f79dc6ee..a599ca43 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -78,7 +78,6 @@ dependencies: image_cropper: ^3.0.3 touchable: ^0.2.1 badges: ^3.1.1 -# buttons_tabbar: ^1.1.2 flutter_month_picker: ^0.0.2 syncfusion_flutter_charts: ^21.2.3 local_auth: ^2.1.6