service request comment done

main_design2.0
zaid_daoud 2 years ago
parent ce143947d1
commit 37a9cd3cc9

@ -112,4 +112,7 @@ class URLs {
static get getPentryStatus => "$_baseUrl/Lookups/GetLookup?lookupEnum=401"; // get
// contacts
static get getPentryContacts => "$_baseUrl/handle/return/all/contacts"; // get
//comments
static get getComments => "$_baseUrl/CallRequest/GetHistoryComments"; // get
static get addComment => "$_baseUrl/CallRequest/AddHistoryComment"; // add
}

@ -0,0 +1,112 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/comment.dart';
import '../../../new_views/common_widgets/app_lazy_loading.dart';
class CommentsProvider extends ChangeNotifier {
// number of items call in each request
final pageItemNumber = 12;
//reset provider data
void reset() {
comments = [];
nextPage = true;
stateCode = null;
}
// state code of current request to defied error message
// like 400 customer request failed
// 500 service not available
int stateCode;
// true if there is next page in product list and false if not
bool nextPage = true;
// list of user requests
List<Comment> comments = [];
// when requests in-process _loading = true
// done _loading = true
// failed _loading = false
bool isLoading;
/// return -2 if request in progress
/// return -1 if error happen when sending request
/// return state code if request complete may be 200, 404 or 403
/// for more details check http state manager
/// lib\controllers\http_status_manger\http_status_manger.dart
Future<int> getComments({@required String callId}) async {
if (isLoading == true) return -2;
isLoading = true;
notifyListeners();
Response response;
try {
response = await ApiManager.instance.get(URLs.getComments + "?callRequestId=$callId");
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List requestsListJson = json.decode(response.body)["data"];
List<Comment> commentsPage = requestsListJson.map((request) => Comment.fromJson(request)).toList();
comments ??= [];
comments.addAll(commentsPage);
if (commentsPage.length == pageItemNumber) {
nextPage = true;
} else {
nextPage = false;
}
}
isLoading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
print(error);
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
/// return -2 if request in progress
/// return -1 if error happen when sending request
/// return state code if request complete may be 200, 404 or 403
/// for more details check http state manager
/// lib\controllers\http_status_manger\http_status_manger.dart
Future<int> addComment(BuildContext context, {@required Comment comment}) async {
if (isLoading == true) return -2;
isLoading = true;
Response response;
try {
comment.id = 0;
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
response = await ApiManager.instance.post(URLs.addComment, body: comment.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
reset(); //visit.status = pentry.ppmVisitStatus;
notifyListeners();
Fluttertoast.showToast(msg: context.translation.successfulRequestMessage);
Navigator.of(context).pop();
} else {
Fluttertoast.showToast(msg: "${context.translation.failedToCompleteRequest} ${jsonDecode(response.body)["message"]}");
}
isLoading = false;
notifyListeners();
Navigator.of(context).pop();
return response.statusCode;
} catch (error) {
print(error);
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -7,6 +7,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:localization/localization.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/asset_transfer_provider.dart';
import 'package:test_sa/controllers/providers/api/comments_provider.dart';
import 'package:test_sa/controllers/providers/api/departments_provider.dart';
import 'package:test_sa/controllers/providers/api/devices_provider.dart';
import 'package:test_sa/controllers/providers/api/gas_refill_provider.dart';
@ -185,6 +186,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => PPMVisitStatusProvider()),
ChangeNotifierProvider(create: (_) => PentryTaskStatusProvider()),
ChangeNotifierProvider(create: (_) => PPMDeviceStatusProvider()),
ChangeNotifierProvider(create: (_) => CommentsProvider()),
],
child: GestureDetector(
onTap: () {

@ -0,0 +1,75 @@
class Comment {
Comment({
this.id,
this.callRequestId,
this.createdOn,
this.createdBy,
this.comment,
});
Comment.fromJson(dynamic json) {
id = json['id'];
callRequestId = json['callRequestId'];
createdOn = json['createdOn'];
createdBy = json['createdBy'] != null ? CreatedBy.fromJson(json['createdBy']) : null;
comment = json['comment'];
}
num id;
num callRequestId;
String createdOn;
CreatedBy createdBy;
String comment;
Comment copyWith({
num id,
num callRequestId,
String createdOn,
CreatedBy createdBy,
String comment,
}) =>
Comment(
id: id ?? this.id,
callRequestId: callRequestId ?? this.callRequestId,
createdOn: createdOn ?? this.createdOn,
createdBy: createdBy ?? this.createdBy,
comment: comment ?? this.comment,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['callRequestId'] = callRequestId;
map['createdOn'] = createdOn;
if (createdBy != null) {
map['createdBy'] = createdBy.toJson();
}
map['comment'] = comment;
return map;
}
}
class CreatedBy {
CreatedBy({
this.userId,
this.userName,
});
CreatedBy.fromJson(dynamic json) {
userId = json['userId'];
userName = json['userName'];
}
String userId;
String userName;
CreatedBy copyWith({
String userId,
String userName,
}) =>
CreatedBy(
userId: userId ?? this.userId,
userName: userName ?? this.userName,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['userId'] = userId;
map['userName'] = userName;
return map;
}
}

@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/comments_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/validator/validator.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/comment.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/loaders/lazy_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_item_found.dart';
import '../../../../new_views/app_style/app_color.dart';
import '../../../widgets/loaders/loading_manager.dart';
class CommentsBottomSheet extends StatefulWidget {
final String requestId;
const CommentsBottomSheet({Key key, @required this.requestId}) : super(key: key);
@override
State<CommentsBottomSheet> createState() => _CommentsBottomSheetState();
}
class _CommentsBottomSheetState extends State<CommentsBottomSheet> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String text;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final commentsProvider = Provider.of<CommentsProvider>(context, listen: false);
final userProvider = Provider.of<UserProvider>(context, listen: false);
return Container(
height: MediaQuery.of(context).size.height * 0.55,
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: const BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)),
),
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight),
child: Form(
key: _formKey,
child: LoadingManager(
isLoading: commentsProvider.isLoading,
isFailedLoading: commentsProvider.comments == null,
stateCode: commentsProvider.stateCode,
onRefresh: () async {
commentsProvider.reset();
await commentsProvider.getComments(callId: widget.requestId);
},
child: Column(
children: [
Container(
width: 40.toScreenWidth,
height: 5.toScreenHeight,
decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)),
),
Align(
alignment: AlignmentDirectional.centerStart,
child: context.translation.comments.heading3(context).custom(fontWeight: FontWeight.w600).paddingOnly(top: 16, bottom: 16),
),
commentsProvider.comments.isEmpty
? NoItemFound(message: context.translation.noServiceRequestFound).expanded
: LazyLoading(
nextPage: commentsProvider.nextPage,
onLazyLoad: () async => await commentsProvider.getComments(callId: widget.requestId),
child: ListView.separated(
itemCount: commentsProvider.comments.length,
separatorBuilder: (cxt, index) => 8.height,
itemBuilder: (context, itemIndex) {
final model = commentsProvider.comments[itemIndex];
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
(model?.createdBy?.userName ?? "Nurse").heading6(context),
8.height,
(model?.comment ?? "").bodyText(context),
8.height,
Align(
alignment: AlignmentDirectional.bottomEnd,
child: DateTime.tryParse(model.createdOn).toIso8601String().toServiceRequestDetailsFormat.tinyFont(context),
),
],
).paddingAll(16),
);
},
),
).expanded,
if (userProvider.user.type == UsersTypes.normal_user) 16.height,
if (userProvider.user.type == UsersTypes.normal_user)
AppTextFormField(
labelText: "Type any comment",
backgroundColor: AppColor.neutral30,
alignLabelWithHint: true,
validator: (value) => Validator.hasValue(value) ? null : context.translation.requiredField,
textInputType: TextInputType.multiline,
suffixIcon: "comment_send".toSvgAsset().paddingOnly(end: 16).onPress(() {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
final comment = Comment(id: 0, callRequestId: num.tryParse(widget.requestId ?? ""), comment: text);
commentsProvider.addComment(context, comment: comment);
}
}),
onSaved: (value) {
text = value;
},
),
16.height,
],
),
),
),
);
}
}

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/comments_provider.dart';
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
@ -15,6 +16,7 @@ import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/app_style/colors.dart';
import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/pages/user/requests/comments_bottom_sheet.dart';
import 'package:test_sa/views/pages/user/requests/update_service_request_page.dart';
import 'package:test_sa/views/pages/user/requests/work_order/work_orders_list_page.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
@ -24,12 +26,23 @@ import 'package:test_sa/views/widgets/sound/sound_player.dart';
import 'first_action_bottom_sheet.dart';
class ServiceRequestDetailsPage extends StatelessWidget {
class ServiceRequestDetailsPage extends StatefulWidget {
static const String id = "/call-details";
ServiceRequest serviceRequest;
ServiceRequestDetailsPage({Key key, this.serviceRequest}) : super(key: key);
@override
State<ServiceRequestDetailsPage> createState() => _ServiceRequestDetailsPageState();
}
class _ServiceRequestDetailsPageState extends State<ServiceRequestDetailsPage> {
@override
void didChangeDependencies() {
Provider.of<CommentsProvider>(context, listen: false).reset();
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
UserProvider _userProvider = Provider.of<UserProvider>(context);
@ -61,31 +74,31 @@ class ServiceRequestDetailsPage extends StatelessWidget {
Row(
children: [
StatusLabel(
label: serviceRequest.priority.name,
id: serviceRequest.priority.id,
textColor: AColors.getPriorityStatusTextColor(serviceRequest.priority.id),
backgroundColor: AColors.getPriorityStatusColor(serviceRequest.priority.id)),
label: widget.serviceRequest.priority.name,
id: widget.serviceRequest.priority.id,
textColor: AColors.getPriorityStatusTextColor(widget.serviceRequest.priority.id),
backgroundColor: AColors.getPriorityStatusColor(widget.serviceRequest.priority.id)),
8.width,
StatusLabel(
label: serviceRequest.statusLabel,
textColor: AColors.getRequestStatusTextColor(serviceRequest.statusValue),
backgroundColor: AColors.getRequestStatusColor(serviceRequest.statusValue)),
label: widget.serviceRequest.statusLabel,
textColor: AColors.getRequestStatusTextColor(widget.serviceRequest.statusValue),
backgroundColor: AColors.getRequestStatusColor(widget.serviceRequest.statusValue)),
1.width.expanded,
],
),
8.height,
Text(serviceRequest.deviceEnName, style: AppTextStyles.heading5.copyWith(color: const Color(0xFF3B3D4A))),
Text(widget.serviceRequest.deviceEnName, style: AppTextStyles.heading5.copyWith(color: const Color(0xFF3B3D4A))),
8.height,
Text(
'${context.translation.assetNumber}: ${serviceRequest.device.assetNumber}',
'${context.translation.assetNumber}: ${widget.serviceRequest.device.assetNumber}',
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
Text(
'Request Type: ${serviceRequest.type.name}',
'Request Type: ${widget.serviceRequest.type.name}',
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
Text(
'Request No: ${serviceRequest.requestCode}',
'Request No: ${widget.serviceRequest.requestCode}',
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
],
@ -95,10 +108,10 @@ class ServiceRequestDetailsPage extends StatelessWidget {
children: [
if (_userProvider.user.type == UsersTypes.normal_user)
"edit".toSvgAsset(width: 48).onPress(() {
Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateServiceRequestPage(serviceRequest: serviceRequest)));
Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateServiceRequestPage(serviceRequest: widget.serviceRequest)));
}),
if (_userProvider.user.type == UsersTypes.engineer) 16.height,
Text(serviceRequest.date.toServiceRequestCardFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: const Color(0xFF3B3D4A))),
Text(widget.serviceRequest.date.toServiceRequestCardFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: const Color(0xFF3B3D4A))),
],
)
],
@ -107,31 +120,31 @@ class ServiceRequestDetailsPage extends StatelessWidget {
const Divider(color: Color(0xFFEAF1F4), height: 1, thickness: 1),
8.height,
Text(
'Manufacture: ${serviceRequest.device.modelDefinition.manufacturerName}',
'Manufacture: ${widget.serviceRequest.device.modelDefinition.manufacturerName}',
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
Text(
'Model: ${serviceRequest.device.modelDefinition.modelName}',
'Model: ${widget.serviceRequest.device.modelDefinition.modelName}',
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
8.height,
if ((serviceRequest.callComments ?? "").isNotEmpty) ...[
if ((widget.serviceRequest.callComments ?? "").isNotEmpty) ...[
const Divider(color: Color(0xFFEAF1F4), height: 1, thickness: 1),
8.height,
Text(
serviceRequest.callComments,
widget.serviceRequest.callComments,
style: AppTextStyles.bodyText.copyWith(color: Color(0xFF757575)),
),
],
if (serviceRequest.devicePhotos.isNotEmpty) ...[
if (widget.serviceRequest.devicePhotos.isNotEmpty) ...[
8.height,
const Divider(color: Color(0xFFEAF1F4), height: 1, thickness: 1),
FilesList(images: serviceRequest.devicePhotos),
FilesList(images: widget.serviceRequest.devicePhotos),
],
if (serviceRequest.audio?.isNotEmpty ?? false) ...[
if (widget.serviceRequest.audio?.isNotEmpty ?? false) ...[
const Divider(color: Color(0xFFEAF1F4), height: 1, thickness: 1),
16.height,
ASoundPlayer(audio: serviceRequest.audio),
ASoundPlayer(audio: widget.serviceRequest.audio),
],
],
).paddingAll(16),
@ -186,16 +199,13 @@ class ServiceRequestDetailsPage extends StatelessWidget {
],
).paddingOnly(bottom: 16, start: 16, end: 16))
.onPress(() async {
// todo 'sikander' add comment bottom sheet
// await showModalBottomSheet(
// context: context,
// useSafeArea: true,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (context) => FirstActionBottomSheet(
// onPressed: () {},
// ),
// );
await showModalBottomSheet(
context: context,
useSafeArea: true,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => CommentsBottomSheet(requestId: widget.serviceRequest.id),
);
}),
],
),
@ -207,12 +217,12 @@ class ServiceRequestDetailsPage extends StatelessWidget {
backgroundColor: const Color(0xfff8f9fb),
body: SafeArea(
child: FutureBuilder(
future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id),
future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: widget.serviceRequest.id),
builder: (context, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const ALoading();
} else if (snap.hasData) {
serviceRequest = snap.data;
final serviceRequest = snap.data;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

Loading…
Cancel
Save