Merge branch 'faiz_development' into mirza_development
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="29.159" height="24.616" viewBox="0 0 29.159 24.616">
|
||||
<g id="promotion" transform="translate(-2 -3.116)">
|
||||
<path id="Path_4626" data-name="Path 4626" d="M26.785,3.408a.292.292,0,0,0-.417-.263L11.455,10.236h.021V20.442h-.021l14.913,7.092a.292.292,0,0,0,.417-.263ZM10.019,20.078V10.6H6.544A4.644,4.644,0,0,0,2,15.339a4.722,4.722,0,0,0,3.191,4.525l2.395,7.663a.292.292,0,0,0,.278.2h3.945a.292.292,0,0,0,.278-.378L9.814,20.078ZM7.728,16.068a.729.729,0,0,0,.729-.729v-.208a.729.729,0,0,0-.729-.729H6.686a.729.729,0,0,0-.729.729v.208a.729.729,0,0,0,.729.729Z" transform="translate(0 0)" fill="#767676" fill-rule="evenodd"/>
|
||||
<path id="Path_4627" data-name="Path 4627" d="M22.29,17.715a5.1,5.1,0,0,0,0-9.224ZM20.832,8.052a5.1,5.1,0,1,0,0,10.1Z" transform="translate(5.953 2.236)" fill="#767676" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 878 B |
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="25.034" height="25.034" viewBox="0 0 25.034 25.034">
|
||||
<g id="weekly-calendar-page-symbol" transform="translate(139)">
|
||||
<path id="Path_4379" data-name="Path 4379" d="M22.53,0H2.5A2.511,2.511,0,0,0,0,2.5V22.53a2.512,2.512,0,0,0,2.5,2.5H22.53a2.512,2.512,0,0,0,2.5-2.5V2.5A2.511,2.511,0,0,0,22.53,0ZM6.756,22.031H2.921V18.62H6.756v3.411Zm0-4.555H2.921v-3.41H6.756v3.41ZM6.467,3.794A1.063,1.063,0,1,1,7.529,2.731,1.062,1.062,0,0,1,6.467,3.794Zm5.406,18.237H8.039V18.62h3.834Zm0-4.555H8.039v-3.41h3.834Zm0-4.552H8.039V9.514h3.834ZM11.455,2.731a1.062,1.062,0,1,1,1.062,1.062A1.062,1.062,0,0,1,11.455,2.731Zm5.54,14.745H13.159v-3.41H17v3.41Zm0-4.552H13.159V9.514H17v3.41ZM17.5,2.731a1.062,1.062,0,1,1,1.062,1.062A1.062,1.062,0,0,1,17.5,2.731Zm4.609,14.745H18.278v-3.41h3.835Zm0-4.552H18.278V9.514h3.835Z" transform="translate(-139)" fill="#767676"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 908 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="25.409" height="25.409" viewBox="0 0 25.409 25.409">
|
||||
<path id="close_3_" data-name="close (3)" d="M10.63,9.119l7-7A1.164,1.164,0,1,0,15.98.477l-7,7-7-7A1.164,1.164,0,0,0,.341,2.123l7,7-7,7a1.164,1.164,0,1,0,1.646,1.646l7-7,7,7a1.164,1.164,0,1,0,1.646-1.646Zm0,0" transform="translate(-0.096 12.609) rotate(-45)" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 382 B |
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="23.889" height="10.642" viewBox="0 0 23.889 10.642">
|
||||
<g id="Group_8170" data-name="Group 8170" transform="translate(-61.5 -50.884)">
|
||||
<path id="Path_66" data-name="Path 66" d="M4.5,18H19.3" transform="translate(58.5 42.025)" fill="none" stroke="#2e303a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/>
|
||||
<path id="Path_67" data-name="Path 67" d="M4.5,9H25.389" transform="translate(58.5 43.384)" fill="none" stroke="#2e303a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 577 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28.123" height="26.245" viewBox="0 0 28.123 26.245">
|
||||
<path id="XMLID_219_" d="M28.984,15.637A2.077,2.077,0,0,1,27.025,17h-.716V27.309a.938.938,0,0,1-.937.937H19.748V21.685a4.687,4.687,0,0,0-9.374,0v6.562H4.749a.938.938,0,0,1-.937-.937V17H3.095a2.1,2.1,0,0,1-1.378-3.675L12.665,2.955a3.486,3.486,0,0,1,4.792,0L28.431,13.347A2.066,2.066,0,0,1,28.984,15.637Z" transform="translate(-0.999 -2.001)" fill="#273139"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 467 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="8.852" height="8.852" viewBox="0 0 8.852 8.852">
|
||||
<path id="clock_7_" data-name="clock (7)" d="M4.426,0A4.426,4.426,0,1,0,8.852,4.426,4.431,4.431,0,0,0,4.426,0Zm2.1,6.715a.368.368,0,0,1-.521,0L4.165,4.871a.367.367,0,0,1-.108-.261v-2.4a.369.369,0,1,1,.738,0V4.458L6.531,6.194A.368.368,0,0,1,6.531,6.715Zm0,0" fill="#a9a9a9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12.375" height="9" viewBox="0 0 12.375 9">
|
||||
<path id="next_3_" data-name="next (3)" d="M13.21,8.1,9.273,4.165a.562.562,0,1,0-.8.8l2.977,2.977H1.563a.562.562,0,1,0,0,1.125h9.892L8.477,12.04a.562.562,0,1,0,.8.8L13.21,8.9a.563.563,0,0,0,0-.8Z" transform="translate(-1 -4)" fill="#06161c"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 342 B |
@ -0,0 +1,6 @@
|
||||
<svg id="bell_2_" data-name="bell (2)" xmlns="http://www.w3.org/2000/svg" width="20.255" height="22.097" viewBox="0 0 20.255 22.097">
|
||||
<path id="Path_4605" data-name="Path 4605" d="M358.833,25.324a.921.921,0,0,1-.921-.921,9.606,9.606,0,0,0-2.831-6.836.921.921,0,0,1,1.3-1.3,11.433,11.433,0,0,1,3.371,8.138A.921.921,0,0,1,358.833,25.324Zm0,0" transform="translate(-339.499 -15.305)" fill="#28323a"/>
|
||||
<path id="Path_4606" data-name="Path 4606" d="M.921,25.324A.921.921,0,0,1,0,24.4a11.434,11.434,0,0,1,3.371-8.138.921.921,0,0,1,1.3,1.3A9.6,9.6,0,0,0,1.841,24.4.921.921,0,0,1,.921,25.324Zm0,0" transform="translate(0 -15.305)" fill="#28323a"/>
|
||||
<path id="Path_4607" data-name="Path 4607" d="M39.174,15.572a6.167,6.167,0,0,1-2.19-4.719V8.286A6.45,6.45,0,0,0,31.46,1.915V.921a.921.921,0,0,0-1.841,0v.994a6.449,6.449,0,0,0-5.524,6.371v2.567a6.174,6.174,0,0,1-2.2,4.726,1.611,1.611,0,0,0,1.048,2.835H38.135a1.611,1.611,0,0,0,1.039-2.842Zm0,0" transform="translate(-20.411)" fill="#28323a"/>
|
||||
<path id="Path_4608" data-name="Path 4608" d="M159.672,450.762A3.458,3.458,0,0,0,163.054,448h-6.765A3.458,3.458,0,0,0,159.672,450.762Zm0,0" transform="translate(-149.544 -428.665)" fill="#28323a"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="25.783" height="30.979" viewBox="0 0 25.783 30.979">
|
||||
<g id="automobile-with-wrench" transform="translate(-30.265)">
|
||||
<path id="Path_4647" data-name="Path 4647" d="M106.524,16.93c2.071,0,6.941-2.493,6.941-8.465,0-.833-.02-1.638-.089-2.4a7.177,7.177,0,0,0-1.182-3.839C111.214.845,109.5,0,106.524,0s-4.69.844-5.669,2.227a7.175,7.175,0,0,0-1.183,3.84c-.069.76-.089,1.565-.089,2.4C99.583,14.437,104.452,16.93,106.524,16.93ZM108.789,3.1l-.506,1.277a5.736,5.736,0,0,0-3.52,0L104.258,3.1A7.109,7.109,0,0,1,108.789,3.1ZM100.615,7.17c.08.175.42.847.946.91.6.073,1.608-2.141,4.963-2.146s4.358,2.219,4.963,2.146c.526-.063.866-.735.946-.91.016.429.022.862.022,1.3a7.548,7.548,0,0,1-2.37,5.732,6.062,6.062,0,0,1-3.56,1.723,6.062,6.062,0,0,1-3.56-1.723,7.547,7.547,0,0,1-2.371-5.732C100.593,8.032,100.6,7.6,100.615,7.17Z" transform="translate(-63.367)" fill="#767676"/>
|
||||
<path id="Path_4648" data-name="Path 4648" d="M56.042,197.513c-.354-2.2-1.084-5.041-2.547-6.05-1-.689-4.486-2.552-5.974-3.347L47.5,188.1a.486.486,0,0,0-.53.046,6.325,6.325,0,0,1-2.539,1.226.486.486,0,0,0-.349.3l-.921,2.429-.922-2.429a.486.486,0,0,0-.349-.3,6.324,6.324,0,0,1-2.539-1.226.487.487,0,0,0-.53-.046c-1.471.786-5,2.687-5.995,3.357-1.684,1.135-2.421,5.241-2.551,6.053a.488.488,0,0,0,.022.24c.036.1.593,1.544,3.379,2.7a1.384,1.384,0,0,1,.615-.442,1.374,1.374,0,0,1-.749-1.955,4.071,4.071,0,0,1,6.959-.316h5.316a4.079,4.079,0,0,1,3.38-1.808h0a4.079,4.079,0,0,1,3.575,2.112A1.374,1.374,0,0,1,52.025,200a1.382,1.382,0,0,1,.624.448c2.779-1.16,3.335-2.6,3.371-2.7A.49.49,0,0,0,56.042,197.513Z" transform="translate(0 -171.901)" fill="#767676"/>
|
||||
<path id="Path_4649" data-name="Path 4649" d="M97.425,297.952h0l-2.561,0a4.839,4.839,0,0,1,0-2.244l2.561,0a.173.173,0,0,0,.151-.257,2.872,2.872,0,0,0-5.184.319H85.634a2.869,2.869,0,0,0-5.185-.31.173.173,0,0,0,.152.256h0l2.561,0a4.839,4.839,0,0,1,0,2.244l-2.561,0a.173.173,0,0,0-.151.256,2.873,2.873,0,0,0,2.517,1.489h0a2.871,2.871,0,0,0,2.663-1.808h6.753a2.876,2.876,0,0,0,2.665,1.808h0a2.874,2.874,0,0,0,2.517-1.5.173.173,0,0,0-.152-.256Z" transform="translate(-45.857 -268.725)" fill="#767676"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="25.211" height="20.945" viewBox="0 0 25.211 20.945">
|
||||
<path id="menu_6_" data-name="menu (6)" d="M-.551,81.687H21.76a1.772,1.772,0,0,1,0,3.484H-.551A1.617,1.617,0,0,1-2,83.429a1.617,1.617,0,0,1,1.45-1.742ZM21.76,83.193H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236H21.76a.24.24,0,0,0,0-.473ZM-.551,72.957H21.76a1.772,1.772,0,0,1,0,3.484H-.551A1.617,1.617,0,0,1-2,74.7a1.617,1.617,0,0,1,1.45-1.742ZM21.76,74.462H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236H21.76a.24.24,0,0,0,0-.473ZM-.551,90.418H21.76a1.772,1.772,0,0,1,0,3.484H-.551A1.617,1.617,0,0,1-2,92.16a1.617,1.617,0,0,1,1.45-1.742ZM21.76,91.923H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236H21.76a.24.24,0,0,0,0-.473ZM-.551,74.935H21.76a.24.24,0,0,0,0-.473H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236ZM21.76,83.193H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236H21.76a.24.24,0,0,0,0-.473Zm0,8.731H-.551a.219.219,0,0,0-.2.236.219.219,0,0,0,.2.236H21.76a.24.24,0,0,0,0-.473Z" transform="translate(2.001 -72.956)" fill="#767676" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -1,195 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
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/extensions/extensions_widget.dart';
|
||||
import 'package:mc_common_app/widgets/txt_field.dart';
|
||||
|
||||
class ChatView extends StatelessWidget {
|
||||
const ChatView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(title: "Chat"),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: 15,
|
||||
separatorBuilder: (BuildContext context, int index) => 20.height,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return ChatMessageCustomWidget(
|
||||
isSent: index.isOdd,
|
||||
profileUrl: MyAssets.bnCar,
|
||||
messageText: "Hi, How Are you? I can help you out with the desired request.",
|
||||
messageTypeEnum: index == 10
|
||||
? (MessageTypeEnum.newOfferRequired)
|
||||
: index == 12
|
||||
? (MessageTypeEnum.offerProvided)
|
||||
: (MessageTypeEnum.text),
|
||||
senderName: "Al Abdullah Cars",
|
||||
);
|
||||
}).horPaddingMain(),
|
||||
),
|
||||
10.width,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: TxtField(
|
||||
// value: adVM.vehicleDemandAmount,
|
||||
// errorValue: adVM.demandAmountError,
|
||||
hint: "Type your message here..",
|
||||
keyboardType: TextInputType.text,
|
||||
isNeedBorder: false,
|
||||
onChanged: (v) => null,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Icon(
|
||||
Icons.send_rounded,
|
||||
color: MyColors.darkPrimaryColor,
|
||||
size: 30,
|
||||
).onPress(() {}))
|
||||
],
|
||||
).toContainer(isShadowEnabled: true),
|
||||
],
|
||||
),
|
||||
// body:
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChatMessageCustomWidget extends StatelessWidget {
|
||||
final String profileUrl;
|
||||
final String senderName;
|
||||
final String messageText;
|
||||
final MessageTypeEnum messageTypeEnum;
|
||||
final bool isSent;
|
||||
|
||||
const ChatMessageCustomWidget({
|
||||
super.key,
|
||||
required this.profileUrl,
|
||||
required this.senderName,
|
||||
required this.messageText,
|
||||
required this.messageTypeEnum,
|
||||
required this.isSent,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: isSent ? TextDirection.rtl : TextDirection.ltr,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Image.asset(
|
||||
profileUrl,
|
||||
width: 34,
|
||||
height: 34,
|
||||
fit: BoxFit.fill,
|
||||
).toCircle(borderRadius: 100),
|
||||
),
|
||||
10.width,
|
||||
Expanded(
|
||||
flex: 10,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"${isSent ? "You" : senderName}".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
5.height,
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: "${messageText}".toText(
|
||||
color: isSent ? MyColors.white : MyColors.lightTextColor,
|
||||
fontSize: 12,
|
||||
// isBold: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (messageTypeEnum == MessageTypeEnum.offerProvided || messageTypeEnum == MessageTypeEnum.newOfferRequired) ...[
|
||||
5.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"40000".toText(fontSize: 19, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
|
||||
],
|
||||
),
|
||||
10.height,
|
||||
if (messageTypeEnum == MessageTypeEnum.newOfferRequired) ...[
|
||||
Center(
|
||||
child: "You asked for the new offer.".toText(
|
||||
color: MyColors.adPendingStatusColor,
|
||||
fontSize: 12,
|
||||
isItalic: true,
|
||||
),
|
||||
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
|
||||
] else ...[
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 27,
|
||||
title: "Accept",
|
||||
fontSize: 9,
|
||||
borderColor: MyColors.greenColor,
|
||||
isFilled: false,
|
||||
onPressed: () {},
|
||||
backgroundColor: MyColors.white,
|
||||
txtColor: MyColors.greenColor,
|
||||
),
|
||||
),
|
||||
20.width,
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 27,
|
||||
title: "Reject",
|
||||
borderColor: MyColors.redColor,
|
||||
isFilled: false,
|
||||
onPressed: () {},
|
||||
backgroundColor: MyColors.white,
|
||||
txtColor: MyColors.redColor,
|
||||
fontSize: 9,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
).toContainer(
|
||||
isShadowEnabled: !isSent,
|
||||
backgroundColor: isSent ? MyColors.darkIconColor : MyColors.white,
|
||||
borderRadius: 0,
|
||||
margin: EdgeInsets.fromLTRB(isSent ? 25 : 0, 0, !isSent ? 25 : 0, 0),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum MessageTypeEnum { text, picture, offerProvided, recording, video, newOfferRequired }
|
||||
@ -1,13 +1,8 @@
|
||||
import 'package:car_customer_app/repositories/provider_repo.dart';
|
||||
import 'package:car_customer_app/repositories/appointment_repo.dart';
|
||||
import 'package:car_customer_app/repositories/request_repo.dart';
|
||||
|
||||
import 'package:mc_common_app/config/dependencies.dart';
|
||||
|
||||
class CustomerDependencies {
|
||||
static void addDependencies() {
|
||||
AppDependencies.addDependencies();
|
||||
injector.registerSingleton<ProviderRepo>(() => ProviderRepoImp());
|
||||
injector.registerSingleton<AppointmentRepo>(() => ScheduleRepoImp());
|
||||
injector.registerSingleton<RequestRepo>(() => RequestRepoImp());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,159 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:mc_common_app/api/api_client.dart';
|
||||
import 'package:mc_common_app/classes/app_state.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/config/dependencies.dart';
|
||||
import 'package:mc_common_app/models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/models/m_response.dart';
|
||||
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
|
||||
import 'package:mc_common_app/models/schedule_model.dart';
|
||||
import 'package:mc_common_app/models/service_schedule_model.dart';
|
||||
import 'package:mc_common_app/utils/enums.dart';
|
||||
|
||||
abstract class AppointmentRepo {
|
||||
Future<Services> getAllServices(String branchId);
|
||||
|
||||
Future<MResponse> createSchedule(Map map);
|
||||
|
||||
Future<MResponse> addServicesInSchedule(Map map);
|
||||
|
||||
Future<MResponse> updateSchedule(Map map);
|
||||
|
||||
Future<List<ScheduleData>> getSchedules(String branchId);
|
||||
|
||||
Future<MResponse> updateServicesInSchedule(Map map);
|
||||
|
||||
Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
|
||||
required List<String> serviceItemIdsForHome,
|
||||
required List<String> serviceItemIdsForWorkshop,
|
||||
});
|
||||
|
||||
Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID});
|
||||
|
||||
Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction});
|
||||
}
|
||||
|
||||
class ScheduleRepoImp implements AppointmentRepo {
|
||||
@override
|
||||
Future<Services> getAllServices(String branchId) async {
|
||||
Map<String, dynamic> map = {"ProviderBranchID": branchId};
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
return await injector.get<ApiClient>().getJsonForObject((json) => Services.fromJson(json), ApiConsts.getServicesOfBranch, token: t, queryParameters: map);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MResponse> createSchedule(Map map) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createSchedule, map, token: t);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MResponse> addServicesInSchedule(Map map) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createGroup, map, token: t);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ScheduleData>> getSchedules(String branchId) async {
|
||||
Map<String, dynamic> map = {"ServiceProviderBranchID": branchId};
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
|
||||
GenericRespModel adsGenericModel = await injector.get<ApiClient>().getJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.getSchedule,
|
||||
token: t,
|
||||
queryParameters: map,
|
||||
);
|
||||
|
||||
return List.generate(adsGenericModel.data.length, (index) => ScheduleData.fromJson(adsGenericModel.data[index]));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MResponse> updateSchedule(Map map) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateSchedule, map, token: t);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MResponse> updateServicesInSchedule(Map map) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateGroup, map, token: t);
|
||||
}
|
||||
|
||||
Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
|
||||
required List<String> serviceItemIdsForHome,
|
||||
required List<String> serviceItemIdsForWorkshop,
|
||||
}) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
var queryParameters = [
|
||||
{
|
||||
"appointmentType": 2,
|
||||
"ServiceItemIDs": serviceItemIdsForHome,
|
||||
},
|
||||
{
|
||||
"appointmentType": 1,
|
||||
"ServiceItemIDs": serviceItemIdsForWorkshop,
|
||||
}
|
||||
];
|
||||
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.GetServiceItemAppointmentScheduleSlots,
|
||||
queryParameters,
|
||||
token: t,
|
||||
);
|
||||
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel =
|
||||
List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
|
||||
return serviceAppointmentScheduleModel;
|
||||
}
|
||||
|
||||
Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID}) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0;
|
||||
|
||||
List<Map<String, dynamic>> mapList = [];
|
||||
schedules.forEach((schedule) {
|
||||
List<int> serviceItemIds = [];
|
||||
schedule.servicesListInAppointment!.forEach((service) {
|
||||
service.serviceItems!.forEach((item) {
|
||||
serviceItemIds.add(item.id!);
|
||||
});
|
||||
});
|
||||
mapList.add({
|
||||
"serviceSlotID": schedule.selectedCustomTimeDateSlotModel!.date!.slotId,
|
||||
"serviceProviderID": serviceProviderID,
|
||||
"customerID": customerId,
|
||||
"serviceItemID": serviceItemIds,
|
||||
});
|
||||
});
|
||||
|
||||
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.ServiceProvidersAppointmentCreate,
|
||||
mapList,
|
||||
token: t,
|
||||
);
|
||||
|
||||
return adsGenericModel;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction}) async {
|
||||
String t = AppState().getUser.data!.accessToken ?? "";
|
||||
|
||||
final payload = {
|
||||
"serviceAppointmentID": serviceAppointmentID,
|
||||
"serviceSlotID": serviceSlotID,
|
||||
"appointmentScheduleAction": appointmentScheduleAction,
|
||||
};
|
||||
|
||||
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.ServiceProviderAppointmentRescheduleCancelAppointment,
|
||||
payload,
|
||||
token: t,
|
||||
);
|
||||
|
||||
return adsGenericModel;
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:mc_common_app/api/api_client.dart';
|
||||
import 'package:mc_common_app/classes/app_state.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/config/dependencies.dart';
|
||||
import 'package:mc_common_app/models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
|
||||
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
|
||||
import 'package:mc_common_app/models/services/item_model.dart';
|
||||
|
||||
abstract class ProviderRepo {
|
||||
Future<List<BranchDetailModel>> getAllNearBranchAndServices();
|
||||
|
||||
Future<List<ItemData>> getServiceItems(int serviceId);
|
||||
|
||||
Future<ProviderProfileModel> getBranchAndServices(int providerId);
|
||||
}
|
||||
|
||||
class ProviderRepoImp implements ProviderRepo {
|
||||
ApiClient apiClient = injector.get<ApiClient>();
|
||||
AppState appState = injector.get<AppState>();
|
||||
|
||||
@override
|
||||
Future<List<BranchDetailModel>> getAllNearBranchAndServices() async {
|
||||
GenericRespModel adsGenericModel = await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.GetAllNearBranches, token: appState.getUser.data!.accessToken);
|
||||
List<BranchDetailModel> nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
|
||||
return nearBranches;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ItemData>> getServiceItems(int serviceId) async {
|
||||
var queryParameters = {
|
||||
"ServiceProviderServiceID": serviceId.toString(),
|
||||
};
|
||||
|
||||
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.getServiceItems,
|
||||
token: appState.getUser.data!.accessToken,
|
||||
queryParameters: queryParameters,
|
||||
);
|
||||
List<ItemData> serviceItems = List.generate(adsGenericModel.data.length, (index) => ItemData.fromJson(adsGenericModel.data[index]));
|
||||
return serviceItems;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ProviderProfileModel> getBranchAndServices(int providerId) async {
|
||||
var postParams = {"serviceProviderID": providerId.toString()};
|
||||
GenericRespModel adsGenericModel =
|
||||
await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.BranchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
|
||||
|
||||
return ProviderProfileModel.fromJson(adsGenericModel.data);
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
import 'package:mc_common_app/api/api_client.dart';
|
||||
import 'package:mc_common_app/classes/app_state.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/config/dependencies.dart';
|
||||
import 'package:mc_common_app/models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/models/requests/offers_model.dart';
|
||||
import 'package:mc_common_app/models/requests/request_model.dart';
|
||||
|
||||
abstract class RequestRepo {
|
||||
Future<GenericRespModel> createRequest(Map<String, dynamic> map);
|
||||
|
||||
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0});
|
||||
|
||||
Future<List<RequestModel>> getRequests(Map<String, dynamic> postParams);
|
||||
}
|
||||
|
||||
class RequestRepoImp implements RequestRepo {
|
||||
ApiClient apiClient = injector.get<ApiClient>();
|
||||
AppState appState = injector.get<AppState>();
|
||||
|
||||
@override
|
||||
Future<GenericRespModel> createRequest(Map<String, dynamic> postParams) async {
|
||||
GenericRespModel enumGenericModel = await apiClient.postJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.createRequest,
|
||||
postParams,
|
||||
token: appState.getUser.data!.accessToken,
|
||||
);
|
||||
return enumGenericModel;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<RequestModel>> getRequests(Map<String, dynamic> postParams) async {
|
||||
GenericRespModel enumGenericModel = await apiClient.postJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.getRequest,
|
||||
postParams,
|
||||
token: appState.getUser.data!.accessToken,
|
||||
);
|
||||
List<RequestModel> requests = List.generate(
|
||||
enumGenericModel.data.length,
|
||||
(index) => RequestModel.fromJson(
|
||||
enumGenericModel.data[index],
|
||||
),
|
||||
);
|
||||
return requests;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0}) async {
|
||||
var queryParameters = {
|
||||
"RequestID": requestId.toString(),
|
||||
"ServiceProviderID": serviceProviderId.toString(),
|
||||
};
|
||||
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
|
||||
(json) => GenericRespModel.fromJson(json),
|
||||
ApiConsts.getRequestOffers,
|
||||
queryParameters: queryParameters,
|
||||
token: appState.getUser.data!.accessToken,
|
||||
);
|
||||
List<OffersModel> offersList = List.generate(
|
||||
genericRespModel.data.length,
|
||||
(index) => OffersModel.fromJson(
|
||||
genericRespModel.data[index],
|
||||
),
|
||||
);
|
||||
return offersList;
|
||||
}
|
||||
}
|
||||
@ -1,677 +0,0 @@
|
||||
import 'package:car_customer_app/repositories/provider_repo.dart';
|
||||
import 'package:car_customer_app/repositories/appointment_repo.dart';
|
||||
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
|
||||
import 'package:car_customer_app/views/appointments/book_appointment_schedules_view.dart';
|
||||
import 'package:car_customer_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
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/models/appointments_models/appointment_list_model.dart';
|
||||
import 'package:mc_common_app/models/enums_model.dart';
|
||||
import 'package:mc_common_app/models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
|
||||
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
|
||||
import 'package:mc_common_app/models/service_schedule_model.dart';
|
||||
import 'package:mc_common_app/models/services/item_model.dart';
|
||||
import 'package:mc_common_app/models/services/service_model.dart';
|
||||
import 'package:mc_common_app/models/widgets_models.dart';
|
||||
import 'package:mc_common_app/repositories/common_repo.dart';
|
||||
import 'package:mc_common_app/services/common_services.dart';
|
||||
import 'package:mc_common_app/theme/colors.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/base_view_model.dart';
|
||||
import 'package:mc_common_app/view_models/payment_view_model.dart';
|
||||
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
|
||||
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AppointmentsVM extends BaseVM {
|
||||
final CommonRepo commonRepo;
|
||||
final CommonAppServices commonServices;
|
||||
final ProviderRepo providerRepo;
|
||||
final AppointmentRepo scheduleRepo;
|
||||
|
||||
AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo});
|
||||
|
||||
bool isFetchingLists = false;
|
||||
|
||||
List<AppointmentListModel> myAppointments = [];
|
||||
List<AppointmentListModel> myUpComingAppointments = [];
|
||||
List<AppointmentListModel> myFilteredAppointments = [];
|
||||
List<FilterListModel> appointmentsFilterOptions = [];
|
||||
|
||||
// List<ScheduleData> availableSchedules = [];
|
||||
|
||||
bool isFetchingServices = false;
|
||||
|
||||
List<DropValue> branchCategories = [];
|
||||
|
||||
bool isHomeTapped = false;
|
||||
|
||||
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = [];
|
||||
|
||||
bool ifItemAlreadySelected(int id) {
|
||||
int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
|
||||
if (indexFound != -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ItemData> allSelectedItemsInAppointments = [];
|
||||
|
||||
Future<void> onItemsSelectedInService() async {
|
||||
if (currentServiceSelection != null) {
|
||||
int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
|
||||
|
||||
if (index == -1) {
|
||||
double totalPrice = 0.0;
|
||||
currentServiceSelection!.serviceItems!.forEach((element) {
|
||||
totalPrice = totalPrice + double.parse(element.price ?? "0.0");
|
||||
});
|
||||
currentServiceSelection!.currentTotalServicePrice = totalPrice;
|
||||
servicesInCurrentAppointment.insert(0, currentServiceSelection!);
|
||||
}
|
||||
|
||||
resetCategorySelectionBottomSheet();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onBookAppointmentPressed(BuildContext context) async {
|
||||
Utils.showLoading(context);
|
||||
bool isSuccess = false;
|
||||
List<int> appointmentIdsList = [];
|
||||
try {
|
||||
GenericRespModel genericRespModel = await scheduleRepo.createServiceAppointment(
|
||||
schedules: serviceAppointmentScheduleList,
|
||||
serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0,
|
||||
);
|
||||
|
||||
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.showToast("${genericRespModel.message.toString()}");
|
||||
return;
|
||||
}
|
||||
if (genericRespModel.data != null) {
|
||||
genericRespModel.data.forEach((element) {
|
||||
if (element['appointmentID'] != 0) {
|
||||
appointmentIdsList.add(element['appointmentID']);
|
||||
isSuccess = true;
|
||||
} else {
|
||||
isSuccess = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
context.read<DashboardVM>().onNavbarTapped(1);
|
||||
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
|
||||
Utils.hideLoading(context);
|
||||
resetAfterBookingAppointment();
|
||||
if (isSuccess) {
|
||||
if (amountToPayForAppointment > 0) {
|
||||
context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: appointmentIdsList);
|
||||
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
|
||||
} else {
|
||||
Utils.showToast("Your appointment has been booked successfully!");
|
||||
getMyAppointments();
|
||||
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Utils.showToast("${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async {
|
||||
context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentId]);
|
||||
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
|
||||
}
|
||||
|
||||
Future<void> onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
|
||||
Utils.showLoading(context);
|
||||
try {
|
||||
GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
|
||||
serviceAppointmentID: appointmentListModel.id ?? 0,
|
||||
serviceSlotID: appointmentListModel.serviceSlotID ?? 0,
|
||||
appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel
|
||||
);
|
||||
|
||||
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.showToast("${genericRespModel.message.toString()}");
|
||||
return;
|
||||
}
|
||||
if (genericRespModel.data == 1) {
|
||||
context.read<DashboardVM>().onNavbarTapped(1);
|
||||
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
|
||||
Utils.showToast("${genericRespModel.message.toString()}");
|
||||
await getMyAppointments();
|
||||
Utils.hideLoading(context);
|
||||
getMyAppointments();
|
||||
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
|
||||
}
|
||||
} catch (e) {
|
||||
Utils.showToast("${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
void updateIsHomeTapped(bool value) {
|
||||
isHomeTapped = value;
|
||||
if (currentServiceSelection != null) {
|
||||
currentServiceSelection!.isHomeSelected = value;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
String pickedHomeLocation = "";
|
||||
|
||||
void updatePickedHomeLocation(String value) {
|
||||
pickedHomeLocation = value;
|
||||
pickHomeLocationError = "";
|
||||
if (currentServiceSelection != null) {
|
||||
currentServiceSelection!.homeLocation = value;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateProviderCategoryId(SelectionModel id) {
|
||||
branchSelectedCategoryId = id;
|
||||
getBranchServices(categoryId: branchSelectedCategoryId.selectedId);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<FilterListModel> providersFilterOptions = [];
|
||||
List<BranchDetailModel> nearbyBranches = [];
|
||||
BranchDetailModel? selectedBranchModel;
|
||||
|
||||
List<ServiceModel> branchServices = [];
|
||||
|
||||
List<ServiceModel> servicesInCurrentAppointment = [];
|
||||
ServiceModel? currentServiceSelection;
|
||||
|
||||
void updateBranchServiceId(SelectionModel id) async {
|
||||
branchSelectedServiceId = id;
|
||||
currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
resetCategorySelectionBottomSheet() {
|
||||
selectedSubServicesCounter = 0;
|
||||
branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
isHomeTapped = false;
|
||||
branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
currentServiceSelection = null;
|
||||
}
|
||||
|
||||
resetAfterBookingAppointment() {
|
||||
servicesInCurrentAppointment.clear();
|
||||
allSelectedItemsInAppointments.clear();
|
||||
serviceAppointmentScheduleList.clear();
|
||||
}
|
||||
|
||||
List<EnumsModel> myAppointmentsEnum = [];
|
||||
|
||||
populateAppointmentsFilterList() async {
|
||||
appointmentsFilterOptions.clear();
|
||||
|
||||
myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums
|
||||
|
||||
for (int i = 0; i < myAppointmentsEnum.length; i++) {
|
||||
appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue));
|
||||
}
|
||||
appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum}) {
|
||||
if (appointmentsFilterOptions.isEmpty) return;
|
||||
for (var value in appointmentsFilterOptions) {
|
||||
value.isSelected = false;
|
||||
}
|
||||
appointmentsFilterOptions[appointmentStatusEnum.getIdFromAppointmentStatusEnum()].isSelected = true;
|
||||
|
||||
if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) {
|
||||
myFilteredAppointments = myAppointments;
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getMyAppointments({bool isNeedToRebuild = false}) async {
|
||||
if (isNeedToRebuild) setState(ViewState.busy);
|
||||
|
||||
myAppointments = await commonRepo.getMyAppointments();
|
||||
myFilteredAppointments = myAppointments;
|
||||
myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList();
|
||||
setState(ViewState.idle);
|
||||
// applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
updateSelectedBranch(BranchDetailModel branchDetailModel) {
|
||||
selectedBranchModel = branchDetailModel;
|
||||
getBranchCategories();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
|
||||
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!.forEach((element) {
|
||||
element.date!.isSelected = false;
|
||||
});
|
||||
|
||||
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true;
|
||||
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex;
|
||||
final date = TimeSlotModel(
|
||||
date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date,
|
||||
slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId,
|
||||
isSelected: true,
|
||||
slot: "",
|
||||
);
|
||||
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) {
|
||||
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!.forEach((element) {
|
||||
element.availableSlots!.forEach((element) => element.isSelected = false);
|
||||
});
|
||||
int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
|
||||
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
|
||||
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
double amountToPayForAppointment = 0.0;
|
||||
double totalAmount = 0.0;
|
||||
|
||||
List<ItemData> serviceItemsFromApi = [];
|
||||
|
||||
ProviderProfileModel? providerProfileModel;
|
||||
|
||||
int selectedSubServicesCounter = 0;
|
||||
|
||||
onItemUpdateOrSelected(int index, bool selected, int itemId) {
|
||||
int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
|
||||
|
||||
serviceItemsFromApi[index].isUpdateOrSelected = selected;
|
||||
serviceItemsFromApi[index].isHomeSelected = isHomeTapped;
|
||||
if (selected) {
|
||||
selectedSubServicesCounter = selectedSubServicesCounter + 1;
|
||||
selectSubServicesError = "";
|
||||
currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
|
||||
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
|
||||
allSelectedItemsInAppointments.forEach((element) {
|
||||
if (!ifItemAlreadySelected(element.id!)) {
|
||||
servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
|
||||
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
|
||||
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!selected) {
|
||||
selectedSubServicesCounter = selectedSubServicesCounter - 1;
|
||||
currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
|
||||
allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
|
||||
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
|
||||
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
|
||||
servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
populateBranchesFilterList() {
|
||||
providersFilterOptions.clear();
|
||||
providersFilterOptions = [
|
||||
FilterListModel(title: "All Providers", isSelected: true, id: 0),
|
||||
FilterListModel(title: "Maintenance", isSelected: false, id: 1),
|
||||
FilterListModel(title: "Oil Service", isSelected: false, id: 2),
|
||||
FilterListModel(title: "Accessories", isSelected: false, id: 3),
|
||||
FilterListModel(title: "Tire Service", isSelected: false, id: 4),
|
||||
FilterListModel(title: "Dent and Paint", isSelected: false, id: 5),
|
||||
];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
applyFilterOnProviders({required int index}) {
|
||||
if (providersFilterOptions.isEmpty) return;
|
||||
for (var value in providersFilterOptions) {
|
||||
value.isSelected = false;
|
||||
}
|
||||
providersFilterOptions[index].isSelected = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
getAllNearBranches({bool isNeedToRebuild = false}) async {
|
||||
//TODO: needs to lat,long into API
|
||||
nearbyBranches.clear();
|
||||
if (isNeedToRebuild) setState(ViewState.busy);
|
||||
nearbyBranches = await providerRepo.getAllNearBranchAndServices();
|
||||
setState(ViewState.idle);
|
||||
}
|
||||
|
||||
Future<List<ItemData>> getServiceItems(int serviceId) async {
|
||||
serviceItemsFromApi.clear();
|
||||
serviceItemsFromApi = await providerRepo.getServiceItems(serviceId);
|
||||
serviceItemsFromApi.forEach((item) {
|
||||
if (ifItemAlreadySelected(item.id!)) {
|
||||
item.isUpdateOrSelected = true;
|
||||
}
|
||||
});
|
||||
setState(ViewState.idle);
|
||||
return serviceItemsFromApi;
|
||||
}
|
||||
|
||||
getBranchAndServices(int providerId) async {
|
||||
providerProfileModel = null;
|
||||
providerProfileModel = await providerRepo.getBranchAndServices(providerId);
|
||||
setState(ViewState.idle);
|
||||
}
|
||||
|
||||
String pickHomeLocationError = "";
|
||||
String selectSubServicesError = "";
|
||||
|
||||
SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
bool isCategoryAlreadyPresent(int id) {
|
||||
final contain = branchCategories.where((element) => element.id == id);
|
||||
if (contain.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void getBranchCategories() async {
|
||||
for (var value in selectedBranchModel!.branchServices!) {
|
||||
if (!isCategoryAlreadyPresent(value.categoryId!)) {
|
||||
branchCategories.add(DropValue(value.categoryId!, value.categoryName!, ""));
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
getBranchServices({required int categoryId}) async {
|
||||
branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
isHomeTapped = false;
|
||||
pickedHomeLocation = "";
|
||||
pickHomeLocationError = "";
|
||||
if (categoryId != -1) {
|
||||
isFetchingServices = true;
|
||||
branchServices = getFilteredBranchServices(categoryId: categoryId);
|
||||
isFetchingServices = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
List<ServiceModel> getFilteredBranchServices({required int categoryId}) {
|
||||
List<ServiceModel> filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList();
|
||||
return filteredServices;
|
||||
}
|
||||
|
||||
void updatePickHomeLocationError(String value) {
|
||||
pickHomeLocationError = value;
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
bool isServiceSelectionValidated() {
|
||||
if (branchSelectedServiceId.selectedId == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isHomeTapped) {
|
||||
if (pickedHomeLocation == "") {
|
||||
updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateItemsSelection() {
|
||||
for (var value in serviceItemsFromApi) {
|
||||
if (value.isUpdateOrSelected!) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
selectSubServicesError = "Please select at least one sub service";
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
|
||||
String getTotalPrice(List<ServiceModel> serviceItems) {
|
||||
var totalPrice = 0.0;
|
||||
serviceItems.forEach((element) {
|
||||
totalPrice = totalPrice + (element.currentTotalServicePrice);
|
||||
});
|
||||
return totalPrice.toString();
|
||||
}
|
||||
|
||||
void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
builder: (BuildContext context) {
|
||||
return AppointmentServicePickBottomSheet();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
builder: (BuildContext context) {
|
||||
double totalKms = 15.3;
|
||||
return InfoBottomSheet(
|
||||
title: "Charges Breakdown".toText(fontSize: 24, isBold: true),
|
||||
description: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Services".toText(fontSize: 16, isBold: true),
|
||||
Column(
|
||||
children: List.generate(
|
||||
selectedService.serviceItems!.length,
|
||||
(index) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
"${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
"${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
if (selectedService.isHomeSelected) ...[
|
||||
20.height,
|
||||
"Home Location".toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
"${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
"${selectedService.rangePricePerKm ?? 0 * totalKms} SAR".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
30.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Total Amount ".toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
(selectedService.isHomeSelected
|
||||
? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}"
|
||||
: "${selectedService.currentTotalServicePrice}")
|
||||
.toText(fontSize: 29, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
30.height,
|
||||
],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
void onReviewButtonPressed(BuildContext context) {
|
||||
bool isValidated = false;
|
||||
|
||||
for (int i = 0; i < serviceAppointmentScheduleList.length; i++) {
|
||||
final schedule = serviceAppointmentScheduleList[i];
|
||||
if (schedule.selectedCustomTimeDateSlotModel == null) {
|
||||
isValidated = false;
|
||||
break;
|
||||
}
|
||||
if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
|
||||
isValidated = false;
|
||||
break;
|
||||
} else {
|
||||
if (schedule.selectedCustomTimeDateSlotModel!.availableSlots == null) {
|
||||
isValidated = true;
|
||||
break;
|
||||
} else {
|
||||
TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected);
|
||||
if (slot.date.isNotEmpty) {
|
||||
isValidated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isValidated) {
|
||||
Utils.showToast("You must select appointment time for each schedule's appointment.");
|
||||
return;
|
||||
}
|
||||
navigateWithName(context, AppRoutes.reviewAppointmentView);
|
||||
}
|
||||
|
||||
void onServicesNextPressed(BuildContext context) async {
|
||||
Utils.showLoading(context);
|
||||
|
||||
List<String> serviceItemIdsForHome = [];
|
||||
List<String> serviceItemIdsForWorkshop = [];
|
||||
allSelectedItemsInAppointments.forEach((serviceItem) {
|
||||
if (serviceItem.isHomeSelected!) {
|
||||
serviceItemIdsForHome.add(serviceItem.id!.toString());
|
||||
} else {
|
||||
serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
|
||||
}
|
||||
});
|
||||
|
||||
serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
|
||||
serviceItemIdsForHome: serviceItemIdsForHome,
|
||||
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
|
||||
);
|
||||
|
||||
if (serviceAppointmentScheduleList.isEmpty) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.showToast("There are no available appointments for selected Items.");
|
||||
return;
|
||||
}
|
||||
totalAmount = 0.0;
|
||||
amountToPayForAppointment = 0.0;
|
||||
serviceAppointmentScheduleList.forEach(
|
||||
(schedule) {
|
||||
amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
|
||||
totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
|
||||
},
|
||||
);
|
||||
Utils.hideLoading(context);
|
||||
|
||||
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
|
||||
Utils.showLoading(context);
|
||||
|
||||
List<String> serviceItemIdsForHome = [];
|
||||
List<String> serviceItemIdsForWorkshop = [];
|
||||
appointmentListModel.appointmentServicesList!.forEach((service) {
|
||||
service.serviceItems!.forEach((serviceItem) {
|
||||
serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
|
||||
|
||||
// if (serviceItem.isHomeSelected ?? false) {
|
||||
// serviceItemIdsForHome.add(serviceItem.id!.toString());
|
||||
// } else {
|
||||
// serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
|
||||
// }
|
||||
});
|
||||
});
|
||||
|
||||
serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
|
||||
serviceItemIdsForHome: serviceItemIdsForHome,
|
||||
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
|
||||
);
|
||||
|
||||
if (serviceAppointmentScheduleList.isEmpty) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.showToast("There are no available appointments for selected Items.");
|
||||
return;
|
||||
}
|
||||
Utils.hideLoading(context);
|
||||
|
||||
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView,
|
||||
arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0)); // 2 For Rescheduling an Appointment
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async {
|
||||
Utils.showLoading(context);
|
||||
try {
|
||||
GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
|
||||
serviceAppointmentID: appointmentId,
|
||||
serviceSlotID: selectedSlotId,
|
||||
appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel
|
||||
);
|
||||
|
||||
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.showToast("${genericRespModel.message.toString()}");
|
||||
return;
|
||||
}
|
||||
if (genericRespModel.data == 1) {
|
||||
context.read<DashboardVM>().onNavbarTapped(1);
|
||||
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
|
||||
Utils.showToast("${genericRespModel.message.toString()}");
|
||||
getMyAppointments();
|
||||
Utils.hideLoading(context);
|
||||
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
|
||||
}
|
||||
} catch (e) {
|
||||
Utils.showToast("${e.toString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'package:mc_common_app/classes/app_state.dart';
|
||||
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
||||
import 'package:mc_common_app/models/user/image_response.dart';
|
||||
import 'package:mc_common_app/repositories/user_repo.dart';
|
||||
import 'package:mc_common_app/services/common_services.dart';
|
||||
import 'package:mc_common_app/utils/utils.dart';
|
||||
import 'package:mc_common_app/view_models/base_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class DashboardVM extends BaseVM {
|
||||
final CommonAppServices commonServices;
|
||||
final UserRepo userRepo;
|
||||
|
||||
DashboardVM({required this.commonServices, required this.userRepo});
|
||||
|
||||
String pickedImage = "";
|
||||
|
||||
int selectedNavbarBarIndex = 2;
|
||||
|
||||
void onNavbarTapped(int index) {
|
||||
selectedNavbarBarIndex = index;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void pickImageFromPhone(BuildContext context, int sourceFlag) async {
|
||||
final File? pickedImageFile = await commonServices.pickImageFromPhone(sourceFlag);
|
||||
if (pickedImageFile == null) {
|
||||
return;
|
||||
}
|
||||
int sizeInBytes = pickedImageFile.lengthSync();
|
||||
if (sizeInBytes > 1000) {
|
||||
Utils.showToast(LocaleKeys.fileLarger.tr());
|
||||
return;
|
||||
} else {
|
||||
String image64 = Utils.convertFileToBase64(pickedImageFile);
|
||||
|
||||
Utils.showLoading(context);
|
||||
ImageResponse response = await userRepo.updateUserImage(image64);
|
||||
Utils.hideLoading(context);
|
||||
Navigator.pop(context);
|
||||
if (response.messageStatus == 1) {
|
||||
Utils.showToast(LocaleKeys.imageUploaded.tr());
|
||||
AppState().getUser.data!.userInfo!.userImageUrl = response.data;
|
||||
} else {
|
||||
Utils.showToast(response.message ?? "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<ImageResponse> updateUserImage(String image) async {
|
||||
return await userRepo.updateUserImage(image);
|
||||
}
|
||||
}
|
||||
@ -1,312 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:car_customer_app/repositories/request_repo.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:mc_common_app/classes/app_state.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart';
|
||||
import 'package:mc_common_app/models/enums_model.dart';
|
||||
import 'package:mc_common_app/models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/models/requests/offers_model.dart';
|
||||
import 'package:mc_common_app/models/requests/request_model.dart';
|
||||
import 'package:mc_common_app/models/widgets_models.dart';
|
||||
import 'package:mc_common_app/repositories/common_repo.dart';
|
||||
import 'package:mc_common_app/services/common_services.dart';
|
||||
import 'package:mc_common_app/utils/enums.dart';
|
||||
import 'package:mc_common_app/utils/utils.dart';
|
||||
import 'package:mc_common_app/view_models/base_view_model.dart';
|
||||
|
||||
class RequestsVM extends BaseVM {
|
||||
final CommonAppServices commonServices;
|
||||
final CommonRepo commonRepo;
|
||||
final RequestRepo requestRepo;
|
||||
|
||||
RequestsVM({required this.commonServices, required this.commonRepo, required this.requestRepo});
|
||||
|
||||
List<RequestModel> myRequests = [];
|
||||
List<RequestModel> myFilteredRequests = [];
|
||||
|
||||
List<FilterListModel> requestsTypeFilterOptions = [];
|
||||
|
||||
List<EnumsModel> myRequestsTypeEnum = [];
|
||||
|
||||
populateRequestsFilterList() async {
|
||||
requestsTypeFilterOptions.clear();
|
||||
|
||||
myRequestsTypeEnum = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 13 is to get Requests Filter Enums
|
||||
|
||||
for (int i = 0; i < myRequestsTypeEnum.length; i++) {
|
||||
requestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue));
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getMyRequests({bool isNeedToRebuild = false}) async {
|
||||
if (isNeedToRebuild) setState(ViewState.busy);
|
||||
|
||||
myRequests = await requestRepo.getRequests(
|
||||
{
|
||||
"customerID": AppState().getUser.data!.userInfo!.customerId,
|
||||
"pageSize": 100,
|
||||
"pageIndex": 0,
|
||||
"requestType": 0,
|
||||
},
|
||||
);
|
||||
applyFilterOnRequestsVM(requestsTypeEnum: RequestsTypeEnum.specialCarRequest);
|
||||
setState(ViewState.idle);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) {
|
||||
if (requestsTypeFilterOptions.isEmpty) return;
|
||||
for (var value in requestsTypeFilterOptions) {
|
||||
value.isSelected = false;
|
||||
}
|
||||
requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeStatusEnum() - 1].isSelected = true; // -1 to match with the index
|
||||
myFilteredRequests = myRequests.where((element) => element.requestType == requestsTypeEnum.getIdFromRequestTypeStatusEnum()).toList();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<File> pickedVehicleImages = [];
|
||||
String vehicleImageError = "";
|
||||
|
||||
void removeImageFromList(String filePath) {
|
||||
int index = pickedVehicleImages.indexWhere((element) => element.path == filePath);
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
pickedVehicleImages.removeAt(index);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void pickMultipleImages() async {
|
||||
List<File> Images = await commonServices.pickMultipleImages();
|
||||
pickedVehicleImages.addAll(Images);
|
||||
if (pickedVehicleImages.isNotEmpty) vehicleImageError = "";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isFetchingRequestType = false;
|
||||
bool isFetchingVehicleType = true;
|
||||
bool isFetchingVehicleDetail = false;
|
||||
List<VehicleTypeModel> vehicleTypes = [];
|
||||
VehicleDetailsModel? vehicleDetails;
|
||||
List<VehicleBrandsModel> vehicleBrands = [];
|
||||
List<VehicleModel> vehicleModels = [];
|
||||
List<VehicleYearModel> vehicleModelYears = [];
|
||||
List<VehicleCountryModel> vehicleCountries = [];
|
||||
List<VehicleCityModel> vehicleCities = [];
|
||||
|
||||
SelectionModel requestTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionRequestTypeId(SelectionModel id) async {
|
||||
requestTypeId = id;
|
||||
getVehicleTypes();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
Future<void> getVehicleTypes() async {
|
||||
resetRequestCreationForm();
|
||||
isFetchingVehicleType = true;
|
||||
vehicleTypes = await commonRepo.getVehicleTypes();
|
||||
isFetchingVehicleType = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
resetRequestCreationForm() {
|
||||
vehicleTypeId.selectedId = -1;
|
||||
vehicleBrandId.selectedId = -1;
|
||||
vehicleModelId.selectedId = -1;
|
||||
vehicleModelYearId.selectedId = -1;
|
||||
vehicleCountryId.selectedId = -1;
|
||||
vehicleCityId.selectedId = -1;
|
||||
}
|
||||
|
||||
void updateSelectionVehicleTypeId(SelectionModel id) async {
|
||||
vehicleTypeId = id;
|
||||
getVehicleBrandsByVehicleTypeId();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getVehicleBrandsByVehicleTypeId() async {
|
||||
// if (vehicleBrandId.selectedId == -1) {
|
||||
// return;
|
||||
// }
|
||||
isFetchingVehicleDetail = true;
|
||||
notifyListeners();
|
||||
vehicleDetails = await commonRepo.getVehicleDetails(vehicleTypeId: vehicleTypeId.selectedId);
|
||||
|
||||
if (vehicleDetails != null) {
|
||||
vehicleBrands = vehicleDetails!.vehicleBrands!;
|
||||
vehicleModels = vehicleDetails!.vehicleModels!;
|
||||
vehicleModelYears = vehicleDetails!.vehicleModelYears!;
|
||||
vehicleCountries = vehicleDetails!.vehicleCountries!;
|
||||
}
|
||||
isFetchingVehicleDetail = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel vehicleBrandId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionVehicleBrandId(SelectionModel id) {
|
||||
vehicleBrandId = id;
|
||||
vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionVehicleModelId(SelectionModel id) {
|
||||
vehicleModelId = id;
|
||||
vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionVehicleModelYearId(SelectionModel id) {
|
||||
vehicleModelYearId = id;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isShippingDeliveryEnabled = false;
|
||||
|
||||
void updateShippingDeliverEnabled(bool v) {
|
||||
isShippingDeliveryEnabled = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isCountryFetching = false;
|
||||
SelectionModel vehicleCountryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionVehicleCountryId(SelectionModel id) async {
|
||||
vehicleCountryId = id;
|
||||
isCountryFetching = true;
|
||||
notifyListeners();
|
||||
vehicleCities = await commonRepo.getVehicleCities(countryId: vehicleCountryId.selectedId);
|
||||
isCountryFetching = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SelectionModel vehicleCityId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
|
||||
|
||||
void updateSelectionVehicleCityId(SelectionModel id) {
|
||||
vehicleCityId = id;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
//Request Management
|
||||
String price = "", description = "";
|
||||
|
||||
updatePrice(String v) {
|
||||
price = v;
|
||||
}
|
||||
|
||||
updateDescription(String v) {
|
||||
description = v;
|
||||
}
|
||||
|
||||
Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async {
|
||||
List<int> imageBytes = await file.readAsBytes();
|
||||
String image = base64Encode(imageBytes);
|
||||
String fileName = file.path.split('/').last;
|
||||
VehiclePostingImages vehiclePostingImages = VehiclePostingImages(
|
||||
imageName: fileName,
|
||||
imageStr: image,
|
||||
imageUrl: file.path,
|
||||
);
|
||||
return vehiclePostingImages;
|
||||
}
|
||||
|
||||
Future<void> onCreateRequestTapped(BuildContext context) async {
|
||||
if (validateCreateRequestForm()) {
|
||||
Utils.showLoading(context);
|
||||
List<VehiclePostingImages> vehicleImages = [];
|
||||
pickedVehicleImages.forEach((element) async {
|
||||
vehicleImages.add(await convertFileToRequestPostingImages(file: element));
|
||||
});
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
"customerID": AppState().getUser.data!.userInfo!.customerId ?? 0,
|
||||
"requestType": requestTypeId.selectedId,
|
||||
"vehicleTypeID": vehicleTypeId.selectedId,
|
||||
"brand": vehicleBrandId.selectedOption,
|
||||
"model": vehicleModelId.selectedOption,
|
||||
"year": vehicleModelYearId.selectedOption,
|
||||
"isNew": true,
|
||||
"countryID": vehicleCountryId.selectedId,
|
||||
"cityID": vehicleCityId.selectedId,
|
||||
"price": price,
|
||||
"description": description,
|
||||
"isSpecialServiceNeeded": false,
|
||||
"requestImages": vehicleImages,
|
||||
};
|
||||
try {
|
||||
GenericRespModel respModel = await requestRepo.createRequest(body);
|
||||
Utils.hideLoading(context);
|
||||
if (respModel.messageStatus == 1) {
|
||||
Utils.showToast("Request Successfully Created");
|
||||
Navigator.pop(context);
|
||||
await getMyRequests();
|
||||
} else {
|
||||
Utils.showToast(respModel.message.toString());
|
||||
}
|
||||
} catch (e, s) {
|
||||
Utils.hideLoading(context);
|
||||
print(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool validateCreateRequestForm() {
|
||||
bool isValid = true;
|
||||
if (requestTypeId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Request Type");
|
||||
isValid = false;
|
||||
} else if (vehicleTypeId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Vehicle Type");
|
||||
isValid = false;
|
||||
} else if (vehicleBrandId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Brand");
|
||||
isValid = false;
|
||||
} else if (vehicleModelId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Model");
|
||||
isValid = false;
|
||||
} else if (vehicleModelYearId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Year");
|
||||
isValid = false;
|
||||
} else if (vehicleCountryId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid Country");
|
||||
isValid = false;
|
||||
} else if (vehicleCityId.selectedId == -1) {
|
||||
Utils.showToast("Please select valid City");
|
||||
isValid = false;
|
||||
} else if (price.isEmpty) {
|
||||
Utils.showToast("Please add valid Price");
|
||||
isValid = false;
|
||||
} else if (description.isEmpty) {
|
||||
Utils.showToast("Please add valid Description");
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
Future<List<OffersModel>> getOffersByRequest({required int requestId, required BuildContext context}) async {
|
||||
try {
|
||||
Utils.showLoading(context);
|
||||
List<OffersModel> respModel = await requestRepo.getOffersByRequest(requestId: requestId);
|
||||
Utils.hideLoading(context);
|
||||
return respModel;
|
||||
} catch (e) {
|
||||
Utils.showToast(e.toString());
|
||||
Utils.hideLoading(context);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,245 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
|
||||
import 'package:mc_common_app/models/services/service_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
|
||||
import 'package:mc_common_app/utils/enums.dart';
|
||||
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/card_button_with_icon.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AppointmentDetailView extends StatelessWidget {
|
||||
final AppointmentListModel appointmentListModel;
|
||||
|
||||
AppointmentDetailView({Key? key, required this.appointmentListModel}) : super(key: key);
|
||||
|
||||
Widget getBaseActionButtonWidget({required Color color, required String text, Color textColor = MyColors.white, required Function() onPressed}) {
|
||||
return Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
title: text,
|
||||
onPressed: onPressed,
|
||||
backgroundColor: color,
|
||||
txtColor: textColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context}) {
|
||||
switch (appointmentStatusEnum) {
|
||||
case AppointmentStatusEnum.booked:
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
children: [
|
||||
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
|
||||
12.width,
|
||||
getBaseActionButtonWidget(
|
||||
color: MyColors.greenColor,
|
||||
onPressed: () {
|
||||
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
|
||||
},
|
||||
text: "Confirm"),
|
||||
],
|
||||
),
|
||||
);
|
||||
case AppointmentStatusEnum.confirmed:
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
children: [
|
||||
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
|
||||
],
|
||||
),
|
||||
);
|
||||
case AppointmentStatusEnum.arrived:
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
children: [
|
||||
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "In Progress"),
|
||||
],
|
||||
),
|
||||
);
|
||||
case AppointmentStatusEnum.cancelled:
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
children: [
|
||||
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"),
|
||||
],
|
||||
),
|
||||
);
|
||||
case AppointmentStatusEnum.allAppointments:
|
||||
return SizedBox();
|
||||
case AppointmentStatusEnum.rescheduled:
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
children: [
|
||||
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
|
||||
12.width,
|
||||
getBaseActionButtonWidget(
|
||||
color: MyColors.greenColor,
|
||||
onPressed: () {
|
||||
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
|
||||
},
|
||||
text: "Confirm"),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void appointmentCancelConfirmationSheet(BuildContext context) {
|
||||
final appointmentsVm = context.read<AppointmentsVM>();
|
||||
return actionConfirmationBottomSheet(
|
||||
context: context,
|
||||
title: "Do you want to cancel this appointment?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
|
||||
subtitle: "Your appointment will be cancelled and you cannot undo this action.",
|
||||
actionButtonYes: Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
title: "Yes",
|
||||
fontSize: 15,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
appointmentsVm.onCancelAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
|
||||
},
|
||||
),
|
||||
),
|
||||
actionButtonNo: Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
isFilled: false,
|
||||
borderColor: MyColors.darkPrimaryColor,
|
||||
title: "No",
|
||||
txtColor: MyColors.darkPrimaryColor,
|
||||
fontSize: 15,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Appointment",
|
||||
profileImageUrl: MyAssets.bnCar,
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
),
|
||||
body: Container(
|
||||
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
appointmentListModel.providerName!.toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
MyAssets.miniClockDark.buildSvg(
|
||||
height: 12,
|
||||
width: 12,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
5.width,
|
||||
"${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}".toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor),
|
||||
],
|
||||
),
|
||||
13.height,
|
||||
if (appointmentListModel.appointmentServicesList != null && appointmentListModel.appointmentServicesList!.isNotEmpty) ...[
|
||||
Column(
|
||||
children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) {
|
||||
ServiceModel service = appointmentListModel.appointmentServicesList![index];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// MyAssets.maintenanceIcon.buildSvg(
|
||||
// height: 10,
|
||||
// width: 10,
|
||||
// fit: BoxFit.fill,
|
||||
// ),
|
||||
// 10.width,
|
||||
"${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true),
|
||||
],
|
||||
),
|
||||
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[
|
||||
Column(
|
||||
children: List.generate(
|
||||
service.serviceItems!.length,
|
||||
(index) => "${service.serviceItems![index].name}".toText(
|
||||
textAlign: TextAlign.start,
|
||||
fontSize: 12,
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
),
|
||||
).paddingOnly(left: 15),
|
||||
],
|
||||
5.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
|
||||
Icon(
|
||||
Icons.arrow_drop_down,
|
||||
size: 30,
|
||||
)
|
||||
],
|
||||
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
15.height,
|
||||
Row(
|
||||
children: [
|
||||
CardButtonWithIcon(
|
||||
title: "Reschedule Appointment",
|
||||
onCardTapped: () {
|
||||
context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
|
||||
},
|
||||
icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
|
||||
),
|
||||
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
|
||||
10.width,
|
||||
CardButtonWithIcon(
|
||||
title: "Pay for Appointment",
|
||||
onCardTapped: () {
|
||||
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
|
||||
},
|
||||
icon: MyAssets.creditCardIcon.buildSvg(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
15.height,
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12),
|
||||
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:car_customer_app/views/appointments/widgets/custom_calender_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/service_schedule_model.dart';
|
||||
import 'package:mc_common_app/models/services/service_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
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/time_slots.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ScreenArgumentsForAppointmentDetailPage {
|
||||
final int routeFlag; // 1 = coming from create appointment || 2 = coming from reschedule appointment
|
||||
final int appointmentId; // 1 = coming from create appointment || 2 = coming from reschedule appointment
|
||||
|
||||
ScreenArgumentsForAppointmentDetailPage({required this.routeFlag, required this.appointmentId});
|
||||
}
|
||||
|
||||
class BookAppointmentSchedulesView extends StatelessWidget {
|
||||
final ScreenArgumentsForAppointmentDetailPage screenArgumentsForAppointmentDetailPage;
|
||||
|
||||
BookAppointmentSchedulesView({Key? key, required this.screenArgumentsForAppointmentDetailPage}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Book Appointment",
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
|
||||
onBackButtonTapped: () => Navigator.pop(context),
|
||||
),
|
||||
body: Consumer(
|
||||
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
21.height,
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: appointmentsVM.serviceAppointmentScheduleList.length,
|
||||
itemBuilder: (BuildContext context, int scheduleIndex) {
|
||||
ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
|
||||
return ExpansionTile(
|
||||
tilePadding: EdgeInsets.symmetric(horizontal: 21, vertical: 10),
|
||||
childrenPadding: EdgeInsets.only(left: 16, bottom: 10, right: 16),
|
||||
title: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: "Schedule ${scheduleIndex + 1}".toText(fontSize: 20, isBold: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
("${scheduleData.appointmentType == 2 ? "Home" : "Workshop"}").toText(fontSize: 12, isBold: true).expand(),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
5.height,
|
||||
ListView.builder(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].servicesListInAppointment!.length,
|
||||
itemBuilder: (BuildContext context, int serviceIndex) {
|
||||
ServiceModel selectedService = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].servicesListInAppointment![serviceIndex];
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ("${serviceIndex + 1}. ${selectedService.providerServiceDescription}").toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
CustomCalenderWidget(customTimeDateSlotList: scheduleData.customTimeDateSlotList ?? [], scheduleIndex: scheduleIndex),
|
||||
if (appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex != null) ...[
|
||||
5.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
("Available Slots").toText(fontSize: 14, isBold: true),
|
||||
],
|
||||
),
|
||||
5.height,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: BuildTimeSlots(
|
||||
timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]
|
||||
.customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ??
|
||||
[],
|
||||
onPressed: (slotIndex) {
|
||||
appointmentsVM.updateSelectedAppointmentSlotByDate(scheduleIndex: scheduleIndex, slotIndex: slotIndex);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10));
|
||||
},
|
||||
).expand(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
txtColor: MyColors.black,
|
||||
maxHeight: 55,
|
||||
title: "Cancel",
|
||||
onPressed: () => Navigator.pop(context),
|
||||
backgroundColor: MyColors.greyButtonColor,
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
title: screenArgumentsForAppointmentDetailPage.routeFlag == 1 ? "Review" : "Confirm",
|
||||
onPressed: () {
|
||||
if (screenArgumentsForAppointmentDetailPage.routeFlag == 1) {
|
||||
appointmentsVM.onReviewButtonPressed(context);
|
||||
} else {
|
||||
appointmentsVM.onRescheduleAppointmentConfirmPressed(
|
||||
context: context,
|
||||
appointmentId: screenArgumentsForAppointmentDetailPage.appointmentId,
|
||||
selectedSlotId: appointmentsVM.serviceAppointmentScheduleList.first.selectedCustomTimeDateSlotModel!.date!.slotId,
|
||||
);
|
||||
}
|
||||
},
|
||||
backgroundColor: MyColors.darkPrimaryColor,
|
||||
),
|
||||
)
|
||||
],
|
||||
).paddingAll(21)
|
||||
],
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/services/item_model.dart';
|
||||
import 'package:mc_common_app/models/services/service_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/views/advertisement/custom_add_button.dart';
|
||||
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/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BookAppointmentServicesView extends StatelessWidget {
|
||||
BookAppointmentServicesView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Book Appointment",
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
|
||||
onBackButtonTapped: () => Navigator.pop(context),
|
||||
),
|
||||
body: Consumer(
|
||||
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
21.height,
|
||||
CustomAddButton(
|
||||
needsBorder: true,
|
||||
bgColor: MyColors.white,
|
||||
onTap: () => appointmentsVM.openTheAddServiceBottomSheet(context, appointmentsVM),
|
||||
text: "Add Services",
|
||||
icon: Container(
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
|
||||
child: const Icon(Icons.add, color: MyColors.white),
|
||||
),
|
||||
).horPaddingMain(),
|
||||
10.height,
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: appointmentsVM.servicesInCurrentAppointment.length,
|
||||
itemBuilder: (BuildContext context, int serviceIndex) {
|
||||
ServiceModel serviceData = appointmentsVM.servicesInCurrentAppointment[serviceIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: (serviceData.serviceDescription ?? "").toText(fontSize: 15, isBold: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (true) ...[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
(serviceData.isHomeSelected ? serviceData.homeLocation : "Workshop").toText(fontSize: 12, isBold: true).expand(),
|
||||
],
|
||||
),
|
||||
5.height,
|
||||
Column(
|
||||
children: List.generate(serviceData.serviceItems!.length, (itemIndex) {
|
||||
ItemData itemData = serviceData.serviceItems![itemIndex];
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"${itemData.name}: ".toText(fontSize: 13, color: MyColors.lightTextColor, isBold: true),
|
||||
("${itemData.price}").toText(fontSize: 13, isBold: true).expand(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
((appointmentsVM.servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
|
||||
Icon(
|
||||
Icons.arrow_drop_down,
|
||||
size: 30,
|
||||
)
|
||||
],
|
||||
).onPress(() => appointmentsVM.priceBreakDownClicked(context, appointmentsVM.servicesInCurrentAppointment[serviceIndex])),
|
||||
],
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10));
|
||||
},
|
||||
).expand(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
txtColor: MyColors.black,
|
||||
maxHeight: 55,
|
||||
title: "Cancel",
|
||||
onPressed: () {
|
||||
appointmentsVM.servicesInCurrentAppointment.clear();
|
||||
appointmentsVM.allSelectedItemsInAppointments.clear();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: MyColors.greyButtonColor,
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
title: "Next",
|
||||
onPressed: () {
|
||||
appointmentsVM.onServicesNextPressed(context);
|
||||
},
|
||||
backgroundColor: MyColors.darkPrimaryColor,
|
||||
),
|
||||
)
|
||||
],
|
||||
).paddingAll(21)
|
||||
],
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1,152 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:car_customer_app/views/appointments/widgets/service_item_with_price_checkbox.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
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/models/services/item_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/utils/utils.dart';
|
||||
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/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BookAppointmentsItemView extends StatelessWidget {
|
||||
const BookAppointmentsItemView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Select Services",
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
|
||||
onBackButtonTapped: () => Navigator.pop(context),
|
||||
),
|
||||
body: Consumer(
|
||||
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
color: MyColors.darkTextColor,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: "${appointmentsVM.selectedSubServicesCounter} Item(s) Selected".toText(fontSize: 16, color: MyColors.white).horPaddingMain(),
|
||||
),
|
||||
16.height,
|
||||
Column(
|
||||
children: [
|
||||
"Few services are not available on home location. Change the location to workshop to full access the services".toText(fontSize: 12, isItalic: true, color: MyColors.lightTextColor),
|
||||
8.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Change location or service: ".toText(fontSize: 14, isBold: true),
|
||||
"Edit".toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor),
|
||||
5.width,
|
||||
MyAssets.icEdit.buildSvg(width: 17),
|
||||
],
|
||||
).onPress(() {}),
|
||||
16.height,
|
||||
Divider(),
|
||||
],
|
||||
).horPaddingMain(),
|
||||
appointmentsVM.serviceItemsFromApi.isEmpty
|
||||
? Expanded(child: Center(child: "No Items to show.".toText(fontSize: 16, color: MyColors.lightTextColor)))
|
||||
: ListView.separated(
|
||||
separatorBuilder: (BuildContext context, int index) => Divider(),
|
||||
itemCount: appointmentsVM.serviceItemsFromApi.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
ItemData itemData = appointmentsVM.serviceItemsFromApi[index];
|
||||
return ServiceItemWithPriceCheckBox(
|
||||
description: itemData.description ?? "Some description about the sub-services",
|
||||
title: itemData.name!,
|
||||
isSelected: itemData.isUpdateOrSelected!,
|
||||
onSelection: (bool value) {
|
||||
print("itemId: ${itemData.id}");
|
||||
appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData.id!);
|
||||
},
|
||||
priceWidget: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// TODO: This Price will be decided according to the service selected
|
||||
itemData.price!.split(".").first.toText(fontSize: 30, isBold: true),
|
||||
" SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).expand(),
|
||||
Column(
|
||||
children: [
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 0.7,
|
||||
),
|
||||
8.height,
|
||||
if (appointmentsVM.selectSubServicesError != "")
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
appointmentsVM.selectSubServicesError.toText(fontSize: 14, color: Colors.red),
|
||||
],
|
||||
).paddingOnly(right: 10),
|
||||
8.height,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
txtColor: MyColors.black,
|
||||
maxHeight: 55,
|
||||
title: "Cancel",
|
||||
onPressed: () {
|
||||
appointmentsVM.resetCategorySelectionBottomSheet();
|
||||
pop(context);
|
||||
},
|
||||
backgroundColor: MyColors.greyButtonColor,
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return ShowFillButton(
|
||||
maxHeight: 55,
|
||||
title: "Next",
|
||||
onPressed: () async {
|
||||
bool resp = appointmentsVM.validateItemsSelection();
|
||||
if (resp) {
|
||||
appointmentsVM.onItemsSelectedInService();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
// bool resp = appointmentsVM.validateItemsSelection();
|
||||
// if (resp) {
|
||||
// Utils.showLoading(context);
|
||||
// await appointmentsVM.mergeServiceIntoAvailableSchedules();
|
||||
// appointmentsVM.resetCategorySelectionBottomSheet();
|
||||
// Utils.hideLoading(context);
|
||||
// Navigator.of(context).pushReplacementNamed(AppRoutes.bookAppointmenServicesView);
|
||||
// }
|
||||
},
|
||||
backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
).horPaddingMain(),
|
||||
16.height,
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
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/provider_details_card.dart';
|
||||
import 'package:mc_common_app/widgets/common_widgets/time_slots.dart';
|
||||
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
|
||||
class BookProviderAppView extends StatefulWidget {
|
||||
const BookProviderAppView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BookProviderAppView> createState() => _BookProviderAppViewState();
|
||||
}
|
||||
|
||||
class _BookProviderAppViewState extends State<BookProviderAppView> {
|
||||
bool isReview = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Appointment",
|
||||
profileImageUrl: MyAssets.bnCar,
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
),
|
||||
body: Container(
|
||||
color: MyColors.backgroundColor,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
ProviderDetailsCard(
|
||||
onCardTapped: () {},
|
||||
providerImageUrl: MyAssets.bnCar,
|
||||
providerLocation: "3km",
|
||||
title: "Al Ahmed Maintenance",
|
||||
providerRatings: "4.9",
|
||||
),
|
||||
12.height,
|
||||
isReview ? ReviewAppointmentSection() : ServicesSelectionSection(),
|
||||
10.height,
|
||||
],
|
||||
),
|
||||
),
|
||||
10.height,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
|
||||
child: ShowFillButton(
|
||||
title: "Book Appointment",
|
||||
maxWidth: double.infinity,
|
||||
onPressed: () {
|
||||
isReview = !isReview;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ServicesSelectionSection extends StatelessWidget {
|
||||
const ServicesSelectionSection({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<DropValue> dropList = [
|
||||
DropValue(0, "Maintenance", ""),
|
||||
DropValue(1, "Car Wash", ""),
|
||||
DropValue(2, "Monthly Checkup", ""),
|
||||
DropValue(3, "Friendly Visit", ""),
|
||||
DropValue(4, "Muftaa", ""),
|
||||
];
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Select services you want".toText(fontSize: 18, isBold: true),
|
||||
8.height,
|
||||
DropdownField(
|
||||
(DropValue value) {},
|
||||
list: dropList,
|
||||
hint: "Select service type",
|
||||
),
|
||||
8.height,
|
||||
DropdownField(
|
||||
(DropValue value) {},
|
||||
list: dropList,
|
||||
hint: "Select service type",
|
||||
),
|
||||
8.height,
|
||||
DropdownField(
|
||||
(DropValue value) {},
|
||||
list: dropList,
|
||||
hint: "Select service type",
|
||||
),
|
||||
22.height,
|
||||
"Select date and time".toText(fontSize: 18, isBold: true),
|
||||
8.height,
|
||||
DropdownField(
|
||||
(DropValue value) {},
|
||||
list: dropList,
|
||||
hint: "Select service type",
|
||||
),
|
||||
22.height,
|
||||
"Available slots".toText(fontSize: 15, isBold: true),
|
||||
8.height,
|
||||
BuildTimeSlots(onPressed: (index) {}, timeSlots: []),
|
||||
22.height,
|
||||
"Total Amount".toText(fontSize: 18, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"3000".toText(fontSize: 20, isBold: true),
|
||||
"SAR".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
|
||||
],
|
||||
),
|
||||
10.height,
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReviewAppointmentSection extends StatelessWidget {
|
||||
const ReviewAppointmentSection({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Review Appointment".toText(fontSize: 18, isBold: true),
|
||||
15.height,
|
||||
"Services".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
|
||||
"Car Engine Check".toText(fontSize: 18, isBold: true),
|
||||
13.height,
|
||||
"Date and Time".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
|
||||
"2 Feb, 2023 at 09:00am".toText(fontSize: 18, isBold: true),
|
||||
13.height,
|
||||
"Total Amount".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"3000".toText(fontSize: 20, isBold: true),
|
||||
"SAR".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
|
||||
],
|
||||
),
|
||||
100.height,
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,307 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.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/schedule_model.dart';
|
||||
import 'package:mc_common_app/models/service_schedule_model.dart';
|
||||
import 'package:mc_common_app/models/services/item_model.dart';
|
||||
import 'package:mc_common_app/models/services/service_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
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/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ReviewAppointment extends StatelessWidget {
|
||||
const ReviewAppointment({Key? key}) : super(key: key);
|
||||
|
||||
Widget buildBranchInfoCard({required BuildContext context}) {
|
||||
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 10,
|
||||
left: 21,
|
||||
right: 21,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
MyAssets.bnCar,
|
||||
width: 80,
|
||||
height: 60,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
(appointmentsVM.selectedBranchModel!.branchName ?? "").toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
children: [
|
||||
LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12),
|
||||
2.width,
|
||||
": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}".toText(fontSize: 12),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"4.9".toText(
|
||||
isUnderLine: true,
|
||||
isBold: true,
|
||||
fontSize: 12,
|
||||
),
|
||||
2.width,
|
||||
MyAssets.starIcon.buildSvg(width: 12),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12));
|
||||
}
|
||||
|
||||
Widget buildServicesInfoCard({required BuildContext context}) {
|
||||
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: appointmentsVM.serviceAppointmentScheduleList.length,
|
||||
itemBuilder: (BuildContext context, int scheduleIndex) {
|
||||
ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: "Services".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListView.separated(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: scheduleData.servicesListInAppointment!.length,
|
||||
itemBuilder: (BuildContext context, int serviceIndex) {
|
||||
String selectedTimeSlot = "";
|
||||
if (scheduleData.selectedCustomTimeDateSlotModel!.availableSlots != null) {
|
||||
selectedTimeSlot = scheduleData.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected).slot;
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
if (scheduleData.servicesListInAppointment!.isNotEmpty) ...[
|
||||
Column(
|
||||
children: List.generate(scheduleData.servicesListInAppointment!.length, (serviceIndex) {
|
||||
ServiceModel serviceData = scheduleData.servicesListInAppointment![serviceIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"${serviceData.providerServiceDescription}".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: List.generate(serviceData.serviceItems!.length, (itemIndex) {
|
||||
ItemData itemData = serviceData.serviceItems![itemIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"${itemData.name}: ${itemData.price} SAR".toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
).paddingOnly(bottom: 10),
|
||||
],
|
||||
5.height,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Time & Location".toText(fontSize: 16, isBold: true),
|
||||
3.height,
|
||||
Row(children: [
|
||||
"Date & Time: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
"${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}".toText(fontSize: 12, isBold: true),
|
||||
]),
|
||||
Row(children: [
|
||||
"Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
|
||||
(scheduleData.appointmentType == 2 ? "Home" : "Workshop").toText(fontSize: 12, isBold: true),
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
10.height,
|
||||
Divider(thickness: 0.7),
|
||||
Builder(builder: (BuildContext context) {
|
||||
double totalServicePrice = 0.0;
|
||||
double totalKms = 15.3;
|
||||
double rangePricePerKm = 5;
|
||||
totalServicePrice = rangePricePerKm * totalKms;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Amount".toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Service Charges".toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
|
||||
"${scheduleData.amountTotal.toString()} SAR".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
if (scheduleData.appointmentType == 1) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Location Charges ($rangePricePerKm x $totalKms )".toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
|
||||
"${totalServicePrice.toString()} SAR".toText(fontSize: 16, isBold: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
10.height,
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2),
|
||||
).paddingOnly(bottom: 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 0));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildNextButtonFooter({required BuildContext context}) {
|
||||
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
|
||||
return Container(
|
||||
color: MyColors.white,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Divider(thickness: 0.7, height: 3),
|
||||
8.height,
|
||||
if (appointmentsVM.amountToPayForAppointment > 0) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Payable now".toText(fontSize: 14, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
appointmentsVM.amountToPayForAppointment.toString().toText(fontSize: 16, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
|
||||
],
|
||||
)
|
||||
],
|
||||
).paddingOnly(left: 21, right: 21),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Remaining Amount".toText(fontSize: 14, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
(appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
|
||||
],
|
||||
)
|
||||
],
|
||||
).paddingOnly(left: 21, right: 21),
|
||||
],
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Total Amount ".toText(fontSize: 18, isBold: true),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true),
|
||||
2.width,
|
||||
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
|
||||
],
|
||||
)
|
||||
],
|
||||
).paddingOnly(left: 21, right: 21),
|
||||
5.height,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
backgroundColor: MyColors.darkPrimaryColor,
|
||||
title: "Book Appointment",
|
||||
onPressed: () {
|
||||
appointmentsVM.onBookAppointmentPressed(context);
|
||||
},
|
||||
).paddingOnly(bottom: 12, left: 21, right: 21),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Review Appointment",
|
||||
isRemoveBackButton: false,
|
||||
isDrawerEnabled: false,
|
||||
onBackButtonTapped: () => Navigator.pop(context),
|
||||
),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListView(
|
||||
children: [
|
||||
buildBranchInfoCard(context: context),
|
||||
buildServicesInfoCard(context: context),
|
||||
],
|
||||
).expand(),
|
||||
buildNextButtonFooter(context: context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,195 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
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/models/widgets_models.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
|
||||
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:mc_common_app/widgets/txt_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AppointmentServicePickBottomSheet extends StatelessWidget {
|
||||
const AppointmentServicePickBottomSheet({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AppointmentsVM appointmentsVM = context.watch<AppointmentsVM>();
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.85,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(8),
|
||||
height: 8,
|
||||
width: 60,
|
||||
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
),
|
||||
12.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Select Category".toText(fontSize: 24, isBold: true),
|
||||
],
|
||||
),
|
||||
30.height,
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return DropdownField(
|
||||
(DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
|
||||
list: appointmentsVM.branchCategories,
|
||||
hint: "Select Category",
|
||||
dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1
|
||||
? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "")
|
||||
: null,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (appointmentsVM.isFetchingServices) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [const CircularProgressIndicator().paddingAll(10)],
|
||||
),
|
||||
] else if (appointmentsVM.branchServices.isNotEmpty) ...[
|
||||
8.height,
|
||||
Builder(
|
||||
builder: (context) {
|
||||
List<DropValue> serviceCategories = [];
|
||||
for (var element in appointmentsVM.branchServices) {
|
||||
if (element.categoryId == appointmentsVM.branchSelectedCategoryId.selectedId) {
|
||||
serviceCategories.add(DropValue(
|
||||
element.serviceProviderServiceId ?? 0,
|
||||
element.serviceDescription!,
|
||||
"",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return DropdownField(
|
||||
(DropValue value) => appointmentsVM.updateBranchServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
|
||||
list: serviceCategories,
|
||||
hint: "Select Service",
|
||||
dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1
|
||||
? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "")
|
||||
: null,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (appointmentsVM.branchSelectedServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[
|
||||
16.height,
|
||||
Row(
|
||||
children: [
|
||||
"Select Service Location".toText(
|
||||
fontSize: 16,
|
||||
isBold: true,
|
||||
color: MyColors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
isFilled: appointmentsVM.isHomeTapped,
|
||||
maxHeight: 48,
|
||||
title: "Home",
|
||||
txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
|
||||
onPressed: () => appointmentsVM.updateIsHomeTapped(true),
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
isFilled: !appointmentsVM.isHomeTapped,
|
||||
txtColor: !appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
|
||||
maxHeight: 48,
|
||||
title: "Workshop",
|
||||
onPressed: () => appointmentsVM.updateIsHomeTapped(false),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (appointmentsVM.isHomeTapped) ...[
|
||||
8.height,
|
||||
TxtField(
|
||||
hint: 'Pick Home Location',
|
||||
errorValue: appointmentsVM.pickHomeLocationError,
|
||||
value: appointmentsVM.pickedHomeLocation,
|
||||
isNeedClickAll: true,
|
||||
postfixData: Icons.location_on,
|
||||
postFixDataColor: MyColors.darkTextColor,
|
||||
onTap: () {
|
||||
//TODO: open the place picked to pick the location and save it in provider.
|
||||
appointmentsVM.updatePickedHomeLocation("PM58+F97, Al Olaya, Riyadh 12333");
|
||||
},
|
||||
),
|
||||
14.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning,
|
||||
color: MyColors.adPendingStatusColor,
|
||||
size: 19,
|
||||
).paddingOnly(bottom: 2),
|
||||
3.width,
|
||||
"Some services are not available on home location.".toText(
|
||||
color: MyColors.adPendingStatusColor,
|
||||
fontSize: 12,
|
||||
isItalic: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[
|
||||
const Divider(thickness: 1, height: 1),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// TODO: This Price will be decided according to the service selected
|
||||
150.toString().toText(fontSize: 30, isBold: true),
|
||||
"SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
|
||||
],
|
||||
),
|
||||
"These charges are additional to the actual service charges. For heavy items the charges may vary.".toText(fontSize: 12, color: MyColors.lightTextColor),
|
||||
22.height,
|
||||
],
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ShowFillButton(
|
||||
maxHeight: 55,
|
||||
backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor,
|
||||
title: "Next",
|
||||
onPressed: () {
|
||||
bool isValidated = appointmentsVM.isServiceSelectionValidated();
|
||||
if (isValidated) {
|
||||
appointmentsVM.getServiceItems(appointmentsVM.branchSelectedServiceId.selectedId);
|
||||
Navigator.pop(context);
|
||||
navigateWithName(context, AppRoutes.bookAppointmentsItemView);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
).paddingOnly(bottom: 20)
|
||||
],
|
||||
)).horPaddingMain();
|
||||
}
|
||||
}
|
||||
@ -1,239 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:car_customer_app/view_models/appointments_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/service_schedule_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class CustomCalenderWidget extends StatefulWidget {
|
||||
final List<CustomTimeDateSlotModel> customTimeDateSlotList;
|
||||
final int scheduleIndex;
|
||||
|
||||
const CustomCalenderWidget({super.key, required this.customTimeDateSlotList, required this.scheduleIndex});
|
||||
|
||||
@override
|
||||
State<CustomCalenderWidget> createState() => _CustomCalenderWidgetState();
|
||||
}
|
||||
|
||||
class _CustomCalenderWidgetState extends State<CustomCalenderWidget> {
|
||||
List<DateTime> allDates = [];
|
||||
List<DropValue> allMonths = [];
|
||||
List<DateTime> datesInSelectedMonth = [];
|
||||
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||
|
||||
late int selectedMonth;
|
||||
late int selectedYear;
|
||||
|
||||
DateTime? _selectedDay;
|
||||
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
populateDateList();
|
||||
}
|
||||
|
||||
populateDateList() {
|
||||
for (var value in widget.customTimeDateSlotList) {
|
||||
DateTime dt = DateFormat('dd MMMM, yyyy').parse(value.date!.date);
|
||||
DropValue dv = DropValue(dt.month, "${dt.month.getMonthNameByNumber()}, ${dt.year}", "${dt.year}");
|
||||
allDates.add(dt);
|
||||
|
||||
if (!ifMonthAlreadyThere(dv)) {
|
||||
allMonths.add(dv);
|
||||
}
|
||||
}
|
||||
selectedMonth = allDates.first.month;
|
||||
selectedYear = allDates.first.year;
|
||||
final appointmentsVM = context.read<AppointmentsVM>();
|
||||
if (appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel != null) {
|
||||
DateTime alreadySelectedDate = DateFormat('dd MMMM, yyyy').parse(appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel!.date!.date);
|
||||
_selectedDay = alreadySelectedDate;
|
||||
_focusedDay = alreadySelectedDate;
|
||||
selectedMonth = alreadySelectedDate.month;
|
||||
selectedYear = alreadySelectedDate.year;
|
||||
}
|
||||
datesInSelectedMonth = allDates.where((element) => element.month == selectedMonth).toList();
|
||||
|
||||
if (appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel == null) {
|
||||
_focusedDay = datesInSelectedMonth.first;
|
||||
}
|
||||
}
|
||||
|
||||
bool ifMonthAlreadyThere(DropValue monthDate) {
|
||||
int index = allMonths.indexWhere((element) => element.id == monthDate.id && element.subValue == monthDate.subValue);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ifDateAlreadyThere(DateTime dt) {
|
||||
int index = allDates.indexWhere((element) => dt.month == element.month && dt.year == element.year);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer(
|
||||
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Builder(builder: (context) {
|
||||
return DropdownField(
|
||||
(DropValue value) {
|
||||
setState(() {
|
||||
selectedYear = int.parse(value.value.split(',')[1]);
|
||||
selectedMonth = value.value.split(',')[0].getMonthNumberByName();
|
||||
datesInSelectedMonth = allDates.where((element) => element.month == selectedMonth).toList();
|
||||
_focusedDay = datesInSelectedMonth.first;
|
||||
});
|
||||
},
|
||||
list: allMonths,
|
||||
dropdownValue: DropValue(selectedMonth, "${selectedMonth.getMonthNameByNumber()}, ${selectedYear}", "${selectedYear}"),
|
||||
hint: "${selectedMonth.getMonthNameByNumber()}, $selectedYear",
|
||||
errorValue: "",
|
||||
showAppointmentPickerVariant: true,
|
||||
);
|
||||
}),
|
||||
),
|
||||
Spacer(),
|
||||
Icon(
|
||||
Icons.calendar_today,
|
||||
color: Colors.black,
|
||||
size: 18,
|
||||
).paddingOnly(right: 10)
|
||||
],
|
||||
).paddingOnly(left: 6, right: 0),
|
||||
TableCalendar(
|
||||
headerVisible: false,
|
||||
firstDay: datesInSelectedMonth.first,
|
||||
lastDay: datesInSelectedMonth.last,
|
||||
focusedDay: _focusedDay,
|
||||
calendarFormat: _calendarFormat,
|
||||
weekendDays: [DateTime.friday, DateTime.saturday],
|
||||
daysOfWeekHeight: 30,
|
||||
availableGestures: AvailableGestures.none,
|
||||
daysOfWeekStyle: DaysOfWeekStyle(
|
||||
weekdayStyle: TextStyle(fontSize: 14, color: MyColors.black),
|
||||
weekendStyle: TextStyle(fontSize: 14, color: MyColors.black.withOpacity(0.5)),
|
||||
),
|
||||
calendarBuilders: CalendarBuilders(
|
||||
selectedBuilder: (BuildContext context, DateTime dateTime1, DateTime dateTime2) {
|
||||
return Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
margin: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: MyColors.darkIconColor,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
dateTime2.day.toString(),
|
||||
style: TextStyle(color: MyColors.white),
|
||||
),
|
||||
);
|
||||
},
|
||||
defaultBuilder: (BuildContext context, DateTime dateTime1, DateTime dateTime2) {
|
||||
int index = datesInSelectedMonth.indexWhere((element) => isSameDay(dateTime1, element));
|
||||
if (index == -1) {
|
||||
return Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
margin: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
dateTime1.day.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
margin: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.orange, width: 2),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
dateTime1.day.toString(),
|
||||
),
|
||||
);
|
||||
},
|
||||
disabledBuilder: (BuildContext context, DateTime dateTime1, DateTime selectedDt) {
|
||||
return Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
margin: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
dateTime1.day.toString(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
headerStyle: HeaderStyle(formatButtonVisible: false),
|
||||
selectedDayPredicate: (day) {
|
||||
// Use `selectedDayPredicate` to determine which day is currently selected.
|
||||
// If this returns true, then `day` will be marked as selected.
|
||||
|
||||
// Using `isSameDay` is recommended to disregard
|
||||
// the time-part of compared DateTime objects.
|
||||
return isSameDay(_selectedDay, day);
|
||||
},
|
||||
onDaySelected: (selectedDay, focusedDay) {
|
||||
int index = datesInSelectedMonth.indexWhere((element) => isSameDay(selectedDay, element));
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
if (!isSameDay(_selectedDay, selectedDay)) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
});
|
||||
|
||||
int dateIndex = widget.customTimeDateSlotList.indexWhere((element) {
|
||||
DateTime dateFromList = DateFormat('dd MMMM, yyyy').parse(element.date!.date);
|
||||
if (isSameDay(dateFromList, selectedDay)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (dateIndex != -1) {
|
||||
appointmentsVM.updateSelectedAppointmentDate(scheduleIndex: widget.scheduleIndex, dateIndex: dateIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,209 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
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/models/appointments_models/appointment_list_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/views/advertisement/custom_add_button.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CustomerAppointmentSliderWidget extends StatelessWidget {
|
||||
final List<AppointmentListModel> myUpComingAppointments;
|
||||
|
||||
const CustomerAppointmentSliderWidget({Key? key, required this.myUpComingAppointments}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (myUpComingAppointments.isEmpty) {
|
||||
return CustomAddButton(
|
||||
needsBorder: true,
|
||||
bgColor: MyColors.white,
|
||||
onTap: () => context.read<DashboardVM>().onNavbarTapped(1),
|
||||
text: "Add New Appointment",
|
||||
icon: Container(
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
|
||||
child: const Icon(Icons.add, color: MyColors.white),
|
||||
),
|
||||
).padding(EdgeInsets.symmetric(vertical: 10, horizontal: 21));
|
||||
return Container(
|
||||
height: 86,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: MyColors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
"Add New Appointment".toText(
|
||||
fontSize: 15,
|
||||
isBold: true,
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
).onPress(() {}).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21, vertical: 10));
|
||||
}
|
||||
return CarouselSlider.builder(
|
||||
options: CarouselOptions(
|
||||
height: 140,
|
||||
viewportFraction: 1.0,
|
||||
enlargeCenterPage: false,
|
||||
enableInfiniteScroll: false,
|
||||
),
|
||||
itemCount: myUpComingAppointments.length,
|
||||
itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => BuildAppointmentContainerForCustomer(
|
||||
isForHome: true,
|
||||
appointmentListModel: myUpComingAppointments[itemIndex],
|
||||
onTapped: () => navigateWithName(context, AppRoutes.appointmentDetailView, arguments: myUpComingAppointments[itemIndex]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BuildAppointmentContainerForCustomer extends StatelessWidget {
|
||||
final bool? isForHome;
|
||||
final AppointmentListModel? appointmentListModel;
|
||||
final Function() onTapped;
|
||||
|
||||
const BuildAppointmentContainerForCustomer({Key? key, this.isForHome = false, required this.onTapped, required this.appointmentListModel}) : super(key: key);
|
||||
|
||||
Widget showServices(String title, String icon, {bool isMoreText = false}) {
|
||||
return Row(
|
||||
children: [
|
||||
if (icon != "") ...[
|
||||
SvgPicture.asset(icon),
|
||||
8.width,
|
||||
],
|
||||
Flexible(
|
||||
child: title.toText(
|
||||
fontSize: 12,
|
||||
isBold: true,
|
||||
color: isMoreText ? MyColors.primaryColor : MyColors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> buildServicesFromAppointment({required AppointmentListModel appointmentListModel}) {
|
||||
if (appointmentListModel.appointmentServicesList == null || appointmentListModel.appointmentServicesList!.isEmpty) {
|
||||
return [SizedBox()];
|
||||
}
|
||||
|
||||
if (appointmentListModel.appointmentServicesList!.length == 1) {
|
||||
return [
|
||||
showServices(
|
||||
appointmentListModel.appointmentServicesList![0].providerServiceDescription,
|
||||
MyAssets.modificationsIcon,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> servicesList = List.generate(
|
||||
2,
|
||||
(index) => showServices(appointmentListModel.appointmentServicesList![index].providerServiceDescription, MyAssets.modificationsIcon),
|
||||
);
|
||||
|
||||
if (appointmentListModel.appointmentServicesList!.length > 1) {
|
||||
servicesList.add(
|
||||
showServices(
|
||||
"+ ${appointmentListModel.appointmentServicesList!.length - 1} More",
|
||||
"",
|
||||
isMoreText: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
return servicesList;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
isForHome != null && isForHome!
|
||||
? Image.asset(
|
||||
MyAssets.bnCar,
|
||||
width: 56,
|
||||
height: 56,
|
||||
fit: BoxFit.fill,
|
||||
).toCircle(borderRadius: 100)
|
||||
: Image.asset(
|
||||
MyAssets.bnCar,
|
||||
width: 80,
|
||||
height: 85,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
8.width,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
(appointmentListModel!.providerName ?? "").toText(color: MyColors.black, isBold: true, fontSize: 16),
|
||||
Row(
|
||||
children: [
|
||||
MyAssets.miniClock.buildSvg(height: 12),
|
||||
2.width,
|
||||
"${appointmentListModel!.duration ?? ""} ${appointmentListModel!.appointmentDate!.toFormattedDateWithoutTime()}".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
9.height,
|
||||
isForHome != null && isForHome!
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"Appointment Details".toText(
|
||||
color: MyColors.primaryColor,
|
||||
isUnderLine: true,
|
||||
isBold: true,
|
||||
fontSize: 14,
|
||||
),
|
||||
const Icon(Icons.arrow_forward),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: buildServicesFromAppointment(appointmentListModel: appointmentListModel!),
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).onPress(onTapped).toWhiteContainer(width: double.infinity, allPading: 12),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
|
||||
class ServiceItemWithPriceCheckBox extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final String title, description;
|
||||
final Widget priceWidget;
|
||||
|
||||
final Function(bool) onSelection;
|
||||
|
||||
const ServiceItemWithPriceCheckBox({
|
||||
required this.isSelected,
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.priceWidget = const SizedBox(),
|
||||
required this.onSelection,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isSelected,
|
||||
onChanged: (bool? v) {
|
||||
onSelection(v ?? false);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
title.toText(fontSize: 16, isBold: true).paddingOnly(top: 10),
|
||||
description.toText(fontSize: 12, color: MyColors.lightTextColor),
|
||||
priceWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsFragment extends StatelessWidget {
|
||||
const SettingsFragment({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
import 'package:car_customer_app/extensions/int_extensions.dart';
|
||||
import 'package:car_customer_app/extensions/string_extensions.dart';
|
||||
import 'package:car_customer_app/theme/colors.dart';
|
||||
import 'package:car_customer_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AdWidget extends StatelessWidget {
|
||||
final int count;
|
||||
|
||||
const AdWidget({Key? key, required this.count}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemCount: count,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 15),
|
||||
child: buildAdContainer(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildAdContainer() {
|
||||
return Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/images/bn_car.jpeg",
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Toyota Crolla".toText(fontSize: 16, isBold: true),
|
||||
Row(
|
||||
children: [
|
||||
"Model:".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
2.width,
|
||||
"2019".toText(),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
"Mileage:".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
2.width,
|
||||
"73,000 km".toText(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Riyadh".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
"9 Hours Ago".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"30,000".toText(fontSize: 16, isBold: true),
|
||||
2.width,
|
||||
"SAR:".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(Icons.arrow_forward)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import 'package:car_customer_app/extensions/int_extensions.dart';
|
||||
import 'package:car_customer_app/extensions/string_extensions.dart';
|
||||
import 'package:car_customer_app/theme/colors.dart';
|
||||
import 'package:car_customer_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppointmentSliderWidget extends StatelessWidget {
|
||||
const AppointmentSliderWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Olaya Brach".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
isBold: true,
|
||||
),
|
||||
"Abdullah Alhbas".toText(
|
||||
isBold: true,
|
||||
fontSize: 14,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
"Appt. On:".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
2.width,
|
||||
"19-Mar-2023 11:48 AM".toText(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
"1+ Requests".toText(fontSize: 10).toContainer(
|
||||
borderRadius: 15,
|
||||
backgroundColor: MyColors.lightGreyEAColor,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 6,
|
||||
horizontal: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
showServices("Maintenance"),
|
||||
2.height,
|
||||
showServices("Accessories and Modification"),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 0);
|
||||
}
|
||||
|
||||
Widget showServices(String title) {
|
||||
return Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.ac_unit,
|
||||
color: MyColors.primaryColor,
|
||||
size: 18,
|
||||
),
|
||||
8.width,
|
||||
title.toText(
|
||||
fontSize: 14,
|
||||
isBold: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import 'package:car_customer_app/extensions/int_extensions.dart';
|
||||
import 'package:car_customer_app/extensions/string_extensions.dart';
|
||||
import 'package:car_customer_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ServiceProviderWidget extends StatelessWidget {
|
||||
const ServiceProviderWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 150,
|
||||
child: ListView.builder(
|
||||
itemCount: 9,
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return SizedBox(
|
||||
width: 90,
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/images/bn_car.jpeg",
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
).toCircle(borderRadius: 100),
|
||||
8.height,
|
||||
"Olaya Branch".toText(fontSize: 14, isBold: true, textAlign: TextAlign.center),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import 'package:car_customer_app/extensions/string_extensions.dart';
|
||||
import 'package:car_customer_app/theme/colors.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class ViewAllWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final String subTitle;
|
||||
|
||||
const ViewAllWidget({Key? key, required this.title, required this.subTitle}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: title.toText(isBold: true, fontSize: 18)),
|
||||
subTitle.toText(
|
||||
fontSize: 11,
|
||||
isBold: true,
|
||||
color: MyColors.primaryColor,
|
||||
textDecoration: TextDecoration.underline,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,234 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/requests_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/widgets_models.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart';
|
||||
import 'package:mc_common_app/views/advertisement/picked_images_container.dart';
|
||||
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/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:mc_common_app/widgets/txt_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CreateRequestPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "Create Request",
|
||||
),
|
||||
body: Consumer<RequestsVM>(builder: (context, requestsVM, widget) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Vehicle Detail".toText(fontSize: 18, isBold: true),
|
||||
8.height,
|
||||
if (requestsVM.isFetchingRequestType) ...[
|
||||
Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
] else ...[
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> requestTypeDrop = [];
|
||||
for (var element in requestsVM.myRequestsTypeEnum) {
|
||||
requestTypeDrop.add(DropValue(element.enumValue.toInt() ?? 0, element.enumValueStr ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: requestTypeDrop,
|
||||
dropdownValue: requestsVM.requestTypeId.selectedId != -1 ? DropValue(requestsVM.requestTypeId.selectedId, requestsVM.requestTypeId.selectedOption, "") : null,
|
||||
hint: "Request Type",
|
||||
errorValue: requestsVM.requestTypeId.errorValue,
|
||||
);
|
||||
}),
|
||||
],
|
||||
8.height,
|
||||
if (requestsVM.requestTypeId.selectedId != -1)
|
||||
if (requestsVM.isFetchingVehicleType) ...[
|
||||
Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
] else ...[
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleTypeDrop = [];
|
||||
for (var element in requestsVM.vehicleTypes) {
|
||||
vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: vehicleTypeDrop,
|
||||
dropdownValue: requestsVM.vehicleTypeId.selectedId != -1 ? DropValue(requestsVM.vehicleTypeId.selectedId, requestsVM.vehicleTypeId.selectedOption, "") : null,
|
||||
hint: "Vehicle Type",
|
||||
errorValue: requestsVM.vehicleTypeId.errorValue,
|
||||
);
|
||||
}),
|
||||
],
|
||||
8.height,
|
||||
if (requestsVM.vehicleTypeId.selectedId != -1)
|
||||
if (requestsVM.isFetchingVehicleDetail) ...[
|
||||
Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
] else ...[
|
||||
Column(
|
||||
children: [
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleBrandsDrop = [];
|
||||
for (var element in requestsVM.vehicleBrands) {
|
||||
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: vehicleBrandsDrop,
|
||||
dropdownValue: requestsVM.vehicleBrandId.selectedId != -1 ? DropValue(requestsVM.vehicleBrandId.selectedId, requestsVM.vehicleBrandId.selectedOption, "") : null,
|
||||
hint: "Brand",
|
||||
errorValue: requestsVM.vehicleBrandId.errorValue,
|
||||
);
|
||||
}),
|
||||
8.height,
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleModelsDrop = [];
|
||||
for (var element in requestsVM.vehicleModels) {
|
||||
if (requestsVM.vehicleBrandId.selectedId == element.vehicleBrandID) vehicleModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.model ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleModelId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: vehicleModelsDrop,
|
||||
dropdownValue: requestsVM.vehicleModelId.selectedId != -1 ? DropValue(requestsVM.vehicleModelId.selectedId, requestsVM.vehicleModelId.selectedOption, "") : null,
|
||||
hint: "Model",
|
||||
errorValue: requestsVM.vehicleModelId.errorValue,
|
||||
);
|
||||
}),
|
||||
8.height,
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleYearModelsDrop = [];
|
||||
for (var element in requestsVM.vehicleModelYears) {
|
||||
vehicleYearModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", ""));
|
||||
}
|
||||
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: vehicleYearModelsDrop,
|
||||
dropdownValue: requestsVM.vehicleModelYearId.selectedId != -1 ? DropValue(requestsVM.vehicleModelYearId.selectedId, requestsVM.vehicleModelYearId.selectedOption, "") : null,
|
||||
hint: "Year",
|
||||
errorValue: requestsVM.vehicleModelYearId.errorValue,
|
||||
);
|
||||
}),
|
||||
8.height,
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleCountriesDrop = [];
|
||||
for (var element in requestsVM.vehicleCountries) {
|
||||
vehicleCountriesDrop.add(DropValue(element.id?.toInt() ?? 0, element.countryName ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleCountryId(SelectionModel(selectedOption: value.value, selectedId: value.id)),
|
||||
list: vehicleCountriesDrop,
|
||||
dropdownValue: requestsVM.vehicleCountryId.selectedId != -1 ? DropValue(requestsVM.vehicleCountryId.selectedId, requestsVM.vehicleCountryId.selectedOption, "") : null,
|
||||
hint: "Country",
|
||||
errorValue: requestsVM.vehicleCountryId.errorValue,
|
||||
);
|
||||
}),
|
||||
if (requestsVM.vehicleCountryId.selectedId != -1) ...[
|
||||
if (requestsVM.isCountryFetching) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [const CircularProgressIndicator().paddingAll(10)],
|
||||
),
|
||||
] else ...[
|
||||
8.height,
|
||||
Builder(builder: (context) {
|
||||
List<DropValue> vehicleCitiesDrop = [];
|
||||
for (var element in requestsVM.vehicleCities) {
|
||||
vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
|
||||
}
|
||||
return DropdownField(
|
||||
(DropValue value) => requestsVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
|
||||
list: vehicleCitiesDrop,
|
||||
dropdownValue: requestsVM.vehicleCityId.selectedId != -1 ? DropValue(requestsVM.vehicleCityId.selectedId, requestsVM.vehicleCityId.selectedOption, "") : null,
|
||||
hint: "City",
|
||||
errorValue: requestsVM.vehicleCityId.errorValue,
|
||||
);
|
||||
}),
|
||||
],
|
||||
],
|
||||
8.height,
|
||||
TxtField(
|
||||
hint: "Price",
|
||||
value: requestsVM.price,
|
||||
onChanged: (e) => requestsVM.updatePrice(e),
|
||||
),
|
||||
8.height,
|
||||
TxtField(
|
||||
hint: "Description",
|
||||
maxLines: 5,
|
||||
value: requestsVM.description,
|
||||
onChanged: (e) => requestsVM.updateDescription(e),
|
||||
),
|
||||
8.height,
|
||||
DottedRectContainer(
|
||||
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
|
||||
text: "Attach Image",
|
||||
icon: MyAssets.attachmentIcon.buildSvg(),
|
||||
),
|
||||
if (requestsVM.vehicleImageError != "") ...[
|
||||
10.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
requestsVM.vehicleImageError.toText(fontSize: 14, color: Colors.red),
|
||||
],
|
||||
).paddingOnly(right: 10)
|
||||
],
|
||||
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
|
||||
16.height,
|
||||
PickedImagesContainer(
|
||||
pickedImages: requestsVM.pickedVehicleImages,
|
||||
onCrossPressedPrimary: requestsVM.removeImageFromList,
|
||||
onAddImagePressed: () {
|
||||
context.read<RequestsVM>().pickMultipleImages();
|
||||
},
|
||||
),
|
||||
],
|
||||
16.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Checkbox(
|
||||
value: requestsVM.isShippingDeliveryEnabled,
|
||||
onChanged: null,
|
||||
|
||||
),
|
||||
"Shipping/Delivery".toText(color: MyColors.darkPrimaryColor, isBold: true, fontSize: 18)
|
||||
],
|
||||
).onPress(() {
|
||||
requestsVM.updateShippingDeliverEnabled(!requestsVM.isShippingDeliveryEnabled);
|
||||
}),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
).toContainer(isShadowEnabled: true, marginAll: 16, paddingAll: 12),
|
||||
),
|
||||
),
|
||||
ShowFillButton(
|
||||
title: "Create Request",
|
||||
maxWidth: double.infinity,
|
||||
margin: EdgeInsets.all(16),
|
||||
onPressed: () async {
|
||||
await context.read<RequestsVM>().onCreateRequestTapped(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
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/models/requests/offers_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
|
||||
class OfferListPage extends StatelessWidget {
|
||||
final List<OffersModel> offersList;
|
||||
|
||||
OfferListPage({required this.offersList});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(title: "Offers"),
|
||||
body: offersList.isEmpty
|
||||
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor))
|
||||
: ListView.separated(
|
||||
itemCount: offersList.length,
|
||||
padding: EdgeInsets.all(16),
|
||||
itemBuilder: (context, index) {
|
||||
OffersModel offersModel = offersList[index];
|
||||
return Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"${offersModel.serviceProvider!.companyName ?? ""}".toText(fontSize: 16, isBold: true),
|
||||
|
||||
//Un read offers count
|
||||
|
||||
// Center(
|
||||
// child: "2".toText(
|
||||
// color: Colors.white,
|
||||
// isBold: true,
|
||||
// fontSize: 10,
|
||||
// ),
|
||||
// ).toContainer(
|
||||
// backgroundColor: MyColors.redColor,
|
||||
// borderRadius: 100,
|
||||
// paddingAll: 1,
|
||||
// width: 20,
|
||||
// height: 20,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: "${offersModel.comment} and This is dummy text edition.".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
"9 Hours Ago".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward,
|
||||
color: MyColors.darkIconColor,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
).onPress(() {
|
||||
navigateWithName(context, AppRoutes.chatView);
|
||||
}).toContainer(isShadowEnabled: true);
|
||||
},
|
||||
separatorBuilder: (context, index) => 16.height,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
import 'package:car_customer_app/view_models/requests_view_model.dart';
|
||||
import 'package:car_customer_app/views/requests/widget/request_status_chip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
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/models/requests/offers_model.dart';
|
||||
import 'package:mc_common_app/models/requests/request_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/utils/utils.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RequestItem extends StatelessWidget {
|
||||
final RequestModel request;
|
||||
|
||||
RequestItem({required this.request});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Utils.statusContainerChip(text: request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)),
|
||||
6.height,
|
||||
"${request.brand} ${request.model}".toText(fontSize: 16, isBold: true),
|
||||
showItem("Model:", "${request.year}"),
|
||||
showItem("Customer Name:", "${request.customerName}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (request.offerCount > 0) ...[
|
||||
Center(
|
||||
child: "${request.offerCount}".toText(
|
||||
color: Colors.white,
|
||||
isBold: true,
|
||||
fontSize: 10,
|
||||
),
|
||||
).toContainer(
|
||||
backgroundColor: MyColors.redColor,
|
||||
borderRadius: 100,
|
||||
paddingAll: 1,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
2.height,
|
||||
request.cityName.toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
"9 Hours Ago".toText(
|
||||
color: MyColors.lightTextColor,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
showItem("Description: ", request.description),
|
||||
showItem("Price Range: ", ""),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
request.price.toString().toText(
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
isBold: true,
|
||||
),
|
||||
2.width,
|
||||
"SAR"
|
||||
.toText(
|
||||
color: MyColors.lightTextColor,
|
||||
fontSize: 10,
|
||||
)
|
||||
.paddingOnly(
|
||||
bottom: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward,
|
||||
color: MyColors.darkIconColor,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).toContainer(isShadowEnabled: true).onPress(() async {
|
||||
List<OffersModel> offers = await context.read<RequestsVM>().getOffersByRequest(requestId: request.id, context: context);
|
||||
navigateWithName(context, AppRoutes.offersListPage, arguments: offers);
|
||||
});
|
||||
}
|
||||
|
||||
Widget showItem(String title, String value) {
|
||||
return Row(
|
||||
children: [
|
||||
title.toText(
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
2.width,
|
||||
value.toText(
|
||||
isBold: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/models/requests/request_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/enums.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
|
||||
class RequestStatusChip extends StatelessWidget {
|
||||
RequestModel request;
|
||||
|
||||
RequestStatusChip(this.request);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return request.requestStatusName
|
||||
.toText(
|
||||
fontSize: 8,
|
||||
color: Colors.white,
|
||||
)
|
||||
.toContainer(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
backgroundColor: request.requestStatus == RequestStatus.pending
|
||||
? MyColors.adPendingStatusColor
|
||||
: request.requestStatus == RequestStatus.cancelled
|
||||
? MyColors.redColor
|
||||
: MyColors.greenColor,
|
||||
borderRadius: 100,
|
||||
);
|
||||
}
|
||||
}
|
||||