Merge branch 'master' into fatima_dev

fatima_dev
Fatimah.Alshammari 2 weeks ago
commit 8efe44b02e

@ -74,6 +74,20 @@
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- App Links: open shared ad links (https://ms.hmg.com/ad/{id}) directly in-app -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="ms.hmg.com" />
</intent-filter>
<!-- Custom scheme fallback used by the ms.hmg.com/ad/{id} web page when App Links verification isn't active -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mowaterprovider" />
</intent-filter>
</activity>

@ -390,6 +390,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 99Z3UD3LJM;
ENABLE_BITCODE = NO;
@ -519,6 +520,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 99Z3UD3LJM;
ENABLE_BITCODE = NO;
@ -543,6 +545,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 99Z3UD3LJM;
ENABLE_BITCODE = NO;

@ -59,5 +59,18 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.cloud.solutions.mowater.ksa.provider</string>
<key>CFBundleURLSchemes</key>
<array>
<string>mowaterprovider</string>
</array>
</dict>
</array>
</dict>
</plist>

@ -7,6 +7,7 @@ import 'package:car_provider_app/view_models/items_view_model.dart';
import 'package:car_provider_app/view_models/schedule_view_model.dart';
import 'package:mc_common_app/repositories/setting_options_repo.dart';
import 'package:mc_common_app/repositories/shipping_repo.dart';
import 'package:mc_common_app/services/ad_deep_link_service.dart';
import 'package:mc_common_app/services/firebase_service.dart';
import 'package:mc_common_app/utils/location/Location.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart';
@ -157,9 +158,22 @@ Future<void> main() async {
// flutter pub run easy_localization:generate --source-dir ./assets/langs -f keys -o locale_keys.g.dart
// command to generate languages data from json
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
AdDeepLinkService.init();
});
}
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);

@ -5,8 +5,10 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -16,12 +18,42 @@ import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class AppointmentDetailListPage extends StatelessWidget {
AppointmentDetailListPage({super.key});
class AppointmentDetailListPage extends StatefulWidget {
const AppointmentDetailListPage({super.key});
@override
State<AppointmentDetailListPage> createState() => _AppointmentDetailListPageState();
}
class _AppointmentDetailListPageState extends State<AppointmentDetailListPage> {
int customerID = 0;
int complainType = 1;
String customerName = "";
bool _isFetching = false;
bool _hasInitialized = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_hasInitialized) return;
_hasInitialized = true;
final tappedAppointment = ModalRoute.of(context)?.settings.arguments;
if (tappedAppointment is AppointmentListModel) {
_isFetching = true;
_fetchFreshAppointment(tappedAppointment);
}
}
Future<void> _fetchFreshAppointment(AppointmentListModel tappedAppointment) async {
int? index = await context.read<AppointmentsVM>().refreshUpcomingAppointmentAndGetIndex(tappedAppointment);
if (!mounted) return;
if (index == null) {
Utils.showToast(LocaleKeys.somethingWrong.tr());
Navigator.pop(context);
return;
}
setState(() => _isFetching = false);
}
@override
Widget build(BuildContext context) {
@ -49,7 +81,7 @@ class AppointmentDetailListPage extends StatelessWidget {
children: [
Expanded(
child: Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
if (appointmentsVM.state == ViewState.busy) {
if (_isFetching || appointmentsVM.state == ViewState.busy) {
return const Center(child: CircularProgressIndicator());
} else {
return ListView.separated(
@ -80,7 +112,7 @@ class AppointmentDetailListPage extends StatelessWidget {
}
}),
),
context.read<AppointmentsVM>().isShowMergeButton()
(!_isFetching && context.read<AppointmentsVM>().isShowMergeButton())
? ShowFillButton(
title: LocaleKeys.mergeAppointments.tr(),
maxWidth: double.infinity,

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:car_provider_app/views/dashboard/widget/general_appointment_widget.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -62,6 +63,7 @@ class _AppointmentPageState extends State<AppointmentPage> {
@override
Widget build(BuildContext context) {
final isArabic = AppState().isArabic(context);
return Scaffold(
appBar: CustomAppBar(
//profileImageUrl: MyAssets.carBanner,
@ -69,7 +71,7 @@ class _AppointmentPageState extends State<AppointmentPage> {
actions: [
Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Padding(
padding: EdgeInsets.only(top: appointmentsVM.appointmentFiltersCounter > 0 ? 20 : 0, right: 21),
padding: EdgeInsets.only(top: appointmentsVM.appointmentFiltersCounter > 0 ? 20 : 0, right: isArabic ? 0 : 16, left: isArabic ? 16 : 0),
child: Badge(
isLabelVisible: appointmentsVM.appointmentFiltersCounter > 0,
largeSize: 15,
@ -152,10 +154,7 @@ class _AppointmentPageState extends State<AppointmentPage> {
isNeedTotalPayment: false,
onTap: () {
appointmentsVM.selectedAppointmentIndex = index;
navigateWithName(
context,
AppRoutes.appointmentDetailList,
);
navigateWithName(context, AppRoutes.appointmentDetailList);
},
);
},
@ -305,3 +304,5 @@ class _AppointmentPageState extends State<AppointmentPage> {
);
}
}

@ -46,7 +46,7 @@ class MergeAppointmentListPage extends StatelessWidget {
isNeedTotalPayment: true,
isSelectable: true,
isNeedToShowAppointmentStatus: true,
isNeedToShowMergeStatus: true,
isNeedToShowMergeStatus: appointmentsVM.myFilteredAppointmentsForProvider[appointmentsVM.selectedAppointmentIndex].customerAppointmentList![index].isMerged ?? false,
onTap: () {
appointmentsVM.updateCheckBoxInMergeRequest(index);
},

@ -1,6 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:car_provider_app/view_models/schedule_view_model.dart';
import 'package:car_provider_app/views/branch_management/branch/dealer/widget/assign_dealer_user_sheet.dart';
import 'package:car_provider_app/views/branch_management/services/services_list_page.dart';
@ -25,7 +23,6 @@ import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/custom_add_button_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/row_with_arrow.dart';
import 'package:provider/provider.dart';
class BranchDetailPage extends StatefulWidget {
@ -156,8 +153,6 @@ class _BranchDetailPageState extends State<BranchDetailPage> {
}
Widget buildServiceTileWidget(int pIndex, ServiceVM serviceVM) {
print("value is ${serviceVM.categories[pIndex].categoryName}");
print("value is ${serviceVM.categories[pIndex].categoryNameN}");
bool isArabic = AppState().isArabic(context);
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -219,7 +214,8 @@ class _BranchDetailPageState extends State<BranchDetailPage> {
width: 13.0,
color: Colors.black,
),
) ],
)
],
)
],
)),
@ -276,7 +272,8 @@ class _BranchDetailPageState extends State<BranchDetailPage> {
),
Column(
children: [
showData("${LocaleKeys.location.tr()}:", "${isArabic ? widget.branchData.cityNameN.toString() : widget.branchData.cityName.toString()}, ${isArabic ? widget.branchData.countryNameN.toString() : widget.branchData.countryName.toString()}"),
showData("${LocaleKeys.location.tr()}:",
"${isArabic ? widget.branchData.cityNameN.toString() : widget.branchData.cityName.toString()}, ${isArabic ? widget.branchData.countryNameN.toString() : widget.branchData.countryName.toString()}"),
showData("${LocaleKeys.provider.tr()}:", widget.branchData.serviceProviderName.toString()),
showData("${LocaleKeys.time.tr()}:", "${widget.branchData.openTime.toString()} - ${widget.branchData.closeTime.toString()}"),
showData("${LocaleKeys.address.tr()}:", widget.branchData.address.toString()),
@ -314,14 +311,7 @@ class _BranchDetailPageState extends State<BranchDetailPage> {
),
)
],
)
// .withArrow(
// isArrowEnabled: true,
// color: Colors.white,
// arrowWidth: 16.16,
// arrowHeight: 11.75,
// )
.margin(right: 5),
).margin(right: 5),
).onPress(() {
if (widget.branchData.branchStatus == BranchStatusEnum.pending || widget.branchData.branchStatus == BranchStatusEnum.rejected) {
Utils.showToast(LocaleKeys.waitForBranchVerification.tr());

@ -77,9 +77,7 @@ class BranchListPage extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(left: 21, right: 21, top: 21),
child: RoleTypeTab(
context.watch<ServiceVM>().selectedBranchStatus == 3
? 0
: context.watch<ServiceVM>().selectedBranchStatus,
context.watch<ServiceVM>().selectedBranchStatus == 3 ? 0 : context.watch<ServiceVM>().selectedBranchStatus,
[
DropValue(
3,

@ -48,6 +48,13 @@ class _CreateServicesPage3State extends State<CreateServicesPage3> {
DropValue? category;
DropValue? service;
bool isEditDisabled = false;
/// Converts chargersPerKm to an integer-valued string (no decimal points) for the API.
String _rangePricePerKmForApi() {
if (chargersPerKm.isEmpty) return "0";
return (double.tryParse(chargersPerKm) ?? 0).toInt().toString();
}
bool isServiceActive = false;
@override
@ -312,7 +319,7 @@ class _CreateServicesPage3State extends State<CreateServicesPage3> {
"isAllowAppointment": isAppointmentAvailable,
"isActive": true,
"customerLocationRange": serviceRage,
"rangePricePerKm": chargersPerKm.isEmpty ? "0.0" : chargersPerKm,
"rangePricePerKm": _rangePricePerKmForApi(),
};
map.add(postParams);
}
@ -345,7 +352,7 @@ class _CreateServicesPage3State extends State<CreateServicesPage3> {
"isAllowAppointment": isAppointmentAvailable,
"isActive": true,
"customerLocationRange": serviceRage,
"rangePricePerKm": chargersPerKm.isEmpty ? "0.0" : chargersPerKm,
"rangePricePerKm": _rangePricePerKmForApi(),
}
];
} else {
@ -403,13 +410,15 @@ class _CreateServicesPage3State extends State<CreateServicesPage3> {
);
return status;
} else {
serviceVM.cannotDeactivateServiceOrCategoryBottomSheet(
log("here cannotDeactivateServiceOrCategoryBottomSheet");
await serviceVM.cannotDeactivateServiceOrCategoryBottomSheet(
mainContext: context,
appointments: list,
branchName: widget.branchModel!.branchName,
isService: true,
);
return !isServiceActive;
// Nothing was actually updated - keep original status, don't pop, don't flip the switch
return false;
}
} catch (e) {
Utils.hideLoading(context);

@ -76,9 +76,9 @@ class HomeFragment extends StatelessWidget {
arguments: value,
),
).toWhiteContainer(width: double.infinity, backgroundColor: Colors.transparent).margin(
left: 21,
right: 21,
);
left: 21,
right: 21,
);
},
),
24.height,

@ -107,22 +107,17 @@ class GeneralAppointmentWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isSelectable)
SizedBox(
child: const SizedBox(
width: 14,
height: 14,
).toContainer(
borderRadius: 24,
marginAll: 4,
paddingAll: 0,
backgroundColor: (appointmentListModel.isSelected ?? false) ? MyColors.primaryColor : MyColors.greyButtonColor,
Container(
height: 20,
width: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (appointmentListModel.isSelected ?? false) ? MyColors.primaryColor : Colors.transparent,
border: Border.all(
color: (appointmentListModel.isSelected ?? false) ? Colors.transparent : MyColors.darkTextColor,
),
),
).toContainer(
borderRadius: 24,
isEnabledBorder: true,
paddingAll: 0,
borderWidget: 3,
borderColor: MyColors.primaryColor,
child: const Center(child: Icon(Icons.check_rounded, size: 16, color: MyColors.white)),
),
if (isSelectable) 12.width,
Expanded(
@ -156,8 +151,8 @@ class GeneralAppointmentWidget extends StatelessWidget {
2.width,
Expanded(
child: appointmentListModel.customerMobileNum.toString().toText(
fontSize: 12,
),
fontSize: 12,
),
),
],
),
@ -171,7 +166,6 @@ class GeneralAppointmentWidget extends StatelessWidget {
Expanded(child: (appointmentListModel.duration ?? "").toText(fontSize: 8))
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -181,9 +175,7 @@ class GeneralAppointmentWidget extends StatelessWidget {
4.width,
// Expanded(child: appointmentListModel.appointmentDate!.toFormattedDateWithoutTime().toText(fontSize: 8))
Expanded(
child: appointmentListModel.appointmentDate!
.toLocalizedFormattedDateWithoutTime(context)
.toText(fontSize: 8),
child: appointmentListModel.appointmentDate!.toLocalizedFormattedDateWithoutTime(context).toText(fontSize: 8),
)
],
),
@ -202,11 +194,9 @@ class GeneralAppointmentWidget extends StatelessWidget {
children: [
"${LocaleKeys.createdOn.tr()}: ".toText(color: MyColors.lightTextColor),
2.width,
// Expanded(child: appointmentListModel.appointmentCreatedOn!.toFormattedDateWithoutTime().toText(fontSize: 8))
// Expanded(child: appointmentListModel.appointmentCreatedOn!.toFormattedDateWithoutTime().toText(fontSize: 8))
Expanded(
child: appointmentListModel.appointmentCreatedOn!
.toLocalizedFormattedDateWithoutTime(context)
.toText(fontSize: 8),
child: appointmentListModel.appointmentCreatedOn!.toLocalizedFormattedDateWithoutTime(context).toText(fontSize: 8),
)
],
),
@ -368,94 +358,3 @@ class GeneralAppointmentWidget extends StatelessWidget {
);
}
}
// https://ms.hmg.com/api/ServiceProviders/ServiceProviderBranchDetail_Get?ID=1061&ServiceCategoryIDs=4
// {ID: 1061, ServiceCategoryIDs: [4]}
// {
// "messageStatus": 1,
// "totalItemsCount": 1,
// "data": [
// {
// "id": 1061,
// "serviceProviderID": 14,
// "serviceProviderName": "James Bond",
// "branchName": "Sawaidi Branch",
// "branchDescription": "This is Swaidi Branch",
// "cityID": 1,
// "cityName": "Riyadh",
// "address": "Riyadh, KSA",
// "latitude": "37.541014800226456",
// "longitude": "-122.36278831958771",
// "distanceKM": 12811.22,
// "openTime": "12:00",
// "closeTime": "22:45",
// "branchStatus": 3,
// "branchStatusText": "ApprovedOrActive",
// "branchRateAvg": 0.0,
// "serviceProviderServices": [
// {
// "serviceProviderServiceID": 1212,
// "providerServiceDescription": null,
// "categoryID": 4,
// "categoryName": "Care and Protection",
// "serviceID": 16,
// "serviceDescription": "film body protection",
// "serviceDescriptionN": "فلم حماية الدهان",
// "serviceStatus": 1,
// "statusText": null,
// "isAllowAppointment": true,
// "isAllowAppointmentHome": true,
// "customerLocationRange": 10,
// "rangePricePerKm": 15.0,
// "itemsCount": 1
// }
// ],
// "serviceProviderBranchImage": [
// {
// "id": 136,
// "imageName": "image_picker_929CDE1A-800C-4EFE-AC48-7060BB338D77-69034-00003ADB833C597D.jpg",
// "imageUrl": "https://ms.hmg.com/api/serviceProviders/ServiceProviderBranchImage?imageName=61aa8beb-99b0-4876-b47a-08dce8674a2c_50243415.png&sp=Provider_14&serviceProviderBranch=ServiceProviderBranch_1061",
// "imageStr": null,
// "serviceProviderBranchID": 1061,
// "serviceProviderBranch": null,
// "isActive": true,
// "createdBy": 101,
// "createdOn": "2024-12-16T14:47:48.6633333",
// "modifiedBy": null,
// "modifiedOn": null
// },
// {
// "id": 137,
// "imageName": "image_picker_EBF6BA32-3E4E-4AAE-BC2C-1B136A1C32B3-69034-00003ADB7C82E00F.jpg",
// "imageUrl": "https://ms.hmg.com/api/serviceProviders/ServiceProviderBranchImage?imageName=61aa8beb-99b0-4876-b47a-08dce8674a2c_51856527.png&sp=Provider_14&serviceProviderBranch=ServiceProviderBranch_1061",
// "imageStr": null,
// "serviceProviderBranchID": 1061,
// "serviceProviderBranch": null,
// "isActive": true,
// "createdBy": 101,
// "createdOn": "2024-12-16T14:47:48.6866667",
// "modifiedBy": null,
// "modifiedOn": null
// },
// {
// "id": 138,
// "imageName": "image_picker_168879D6-4707-4D0E-ACDA-0C956BD2AC4A-69034-00003AE6922D39CF.jpg",
// "imageUrl": "https://ms.hmg.com/api/serviceProviders/ServiceProviderBranchImage?imageName=61aa8beb-99b0-4876-b47a-08dce8674a2c_61115226.png&sp=Provider_14&serviceProviderBranch=ServiceProviderBranch_1061",
// "imageStr": null,
// "serviceProviderBranchID": 1061,
// "serviceProviderBranch": null,
// "isActive": true,
// "createdBy": 101,
// "createdOn": "2024-12-16T14:47:48.7066667",
// "modifiedBy": null,
// "modifiedOn": null
// }
// ]
// }
// ],
// "message": "Success"
// }

@ -45,9 +45,9 @@ class _MySubscriptionsPageState extends State<MySubscriptionsPage> {
totalAds: sub.totalAds ?? 0,
totalBranches: sub.totalBranches ?? 0,
totalSubUsers: sub.totalSubUsers ?? 0,
countryId: 1,
countryName: "KSA",
currency: "SAR",
countryId: sub.countryId ?? 0,
countryName: sub.countryName ?? "KSA",
currency: sub.currency ?? "KSA",
price: sub.price,
dateEnd: sub.dateEnd,
dateStart: sub.dateStart,

@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
@ -209,3 +208,5 @@ class _UpgradePackageSheetState extends State<UpgradePackageSheet> {
);
}
}

@ -93,7 +93,7 @@ class AllSubscriptionsCard extends StatelessWidget {
],
),
),
if (subscription.subscriptionTypeEnum == SubscriptionTypeEnum.current && subscription.isRenewable!)
if (subscription.subscriptionTypeEnum == SubscriptionTypeEnum.current && subscription.isRenewable! && DateHelper.isWithinDays(subscription.dateEnd, 7))
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,

@ -105,7 +105,7 @@ class MySubscriptionsCard extends StatelessWidget {
"${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateFormat('d MMMM yyyy', context.locale.toString()).format(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}"
.toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium),
// "${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}".toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium),
if (subscriptionVM.isRenewable!)
if (subscriptionVM.isRenewable! && DateHelper.isWithinDays(subscriptionVM.dateEnd, 7))
Row(
children: [
6.width,

Loading…
Cancel
Save