Merge remote-tracking branch 'origin/zaid_development_new' into majd_development_new
# Conflicts: # lib/views/pages/sub_workorder/search_sub_workorder_page.dartpull/2/head
commit
47e744f481
@ -0,0 +1,162 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
|
||||
import 'package:test_sa/models/service_request/search_work_order.dart';
|
||||
import 'package:test_sa/views/app_style/colors.dart';
|
||||
import 'package:test_sa/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart';
|
||||
import 'package:test_sa/views/pages/sub_workorder/work_order_details_bottom_sheet.dart';
|
||||
|
||||
import '../../../controllers/api_routes/http_status_manger.dart';
|
||||
import '../../../controllers/localization/localization.dart';
|
||||
import '../../../models/subtitle.dart';
|
||||
import '../../widgets/buttons/app_back_button.dart';
|
||||
import '../../widgets/buttons/app_button.dart';
|
||||
|
||||
class CreateSubWorkOrderPage extends StatefulWidget {
|
||||
static const id = "/CreateSubWorkOrder";
|
||||
final SearchWorkOrders workOrder;
|
||||
const CreateSubWorkOrderPage({this.workOrder, Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CreateSubWorkOrderPage> createState() => _CreateSubWorkOrderPageState();
|
||||
}
|
||||
|
||||
class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Subtitle subtitle = AppLocalization.of(context).subtitle;
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: AColors.primaryColor,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const ABackButton(),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"New Work Order",
|
||||
style: Theme.of(context).textTheme.headline6.copyWith(color: AColors.white, fontStyle: FontStyle.italic),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 48,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => WorkOrderDetailsBottomSheet(workOrder: widget.workOrder),
|
||||
);
|
||||
},
|
||||
child: Card(
|
||||
child: ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Text(
|
||||
"WO Details",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Text("*", style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 14, color: AColors.primaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => SparePartsBottomSheet(workOrder: widget.workOrder),
|
||||
);
|
||||
},
|
||||
child: Card(
|
||||
child: ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Spare Parts",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Text("*", style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 14, color: AColors.primaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: AButton(
|
||||
text: subtitle.create,
|
||||
onPressed: () async {
|
||||
// validate = true;
|
||||
// if (!_formKey.currentState.validate()) {
|
||||
// setState(() {});
|
||||
// return;
|
||||
// }
|
||||
// if (!_serviceReport.validate()) return;
|
||||
_formKey.currentState.save();
|
||||
|
||||
// _isLoading = true;
|
||||
setState(() {});
|
||||
|
||||
int status = await Provider.of<ServiceRequestsProvider>(context).createServiceReport(
|
||||
// user: _userProvider.user,
|
||||
// host: _settingProvider.host,
|
||||
// report: _serviceReport,
|
||||
// request: widget.request,
|
||||
);
|
||||
// _isLoading = false;
|
||||
setState(() {});
|
||||
if (status >= 200 && status < 300) {
|
||||
Fluttertoast.showToast(msg: subtitle.requestCompleteSuccessfully);
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: subtitle);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(errorMessage),
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/models/service_request/search_work_order.dart';
|
||||
import 'package:test_sa/views/app_style/colors.dart';
|
||||
|
||||
import '../../../controllers/api_routes/http_status_manger.dart';
|
||||
import '../../../controllers/localization/localization.dart';
|
||||
import '../../../controllers/providers/api/service_requests_provider.dart';
|
||||
import '../../../models/service_report.dart';
|
||||
import '../../../models/subtitle.dart';
|
||||
import '../../widgets/app_text_form_field.dart';
|
||||
import '../../widgets/buttons/app_button.dart';
|
||||
import '../../widgets/titles/app_sub_title.dart';
|
||||
|
||||
class SparePartsBottomSheet extends StatefulWidget {
|
||||
final SearchWorkOrders workOrder;
|
||||
const SparePartsBottomSheet({this.workOrder, Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SparePartsBottomSheet> createState() => _SparePartsBottomSheetState();
|
||||
}
|
||||
|
||||
class _SparePartsBottomSheetState extends State<SparePartsBottomSheet> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
SearchWorkOrders _workOrder;
|
||||
bool _isLoading = false;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_workOrder = widget.workOrder;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
final Subtitle subtitle = AppLocalization.of(context).subtitle;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
topRight: Radius.circular(15),
|
||||
),
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
height: size.height * 0.9,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ASubTitle("Spare Parts"),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SingleChildScrollView(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
if (_workOrder.sparePartsWorkOrders?.length != null)
|
||||
ListView.builder(
|
||||
itemCount: _workOrder.sparePartsWorkOrders?.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final wo = _workOrder.sparePartsWorkOrders[index];
|
||||
return Column(
|
||||
children: [
|
||||
const ATextFormField(labelText: "Part No."),
|
||||
const SizedBox(height: 8),
|
||||
const ATextFormField(
|
||||
labelText: "Description",
|
||||
hintText: "Add Some Text",
|
||||
textInputType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const ATextFormField(
|
||||
labelText: "Quantity",
|
||||
textInputType: TextInputType.number,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const Icon(Icons.delete, color: Colors.red),
|
||||
Text(
|
||||
"Remove this part",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AColors.primaryColor.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: AColors.primaryColor),
|
||||
),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
"Add Part",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: AColors.primaryColor,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.add_circle,
|
||||
color: AColors.primaryColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AButton(
|
||||
text: subtitle.update,
|
||||
onPressed: () async {
|
||||
// _validate = true;
|
||||
if (!_formKey.currentState.validate()) {
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
// if (!_serviceReport.validate()) {
|
||||
// setState(() {});
|
||||
// return;
|
||||
// }
|
||||
_formKey.currentState.save();
|
||||
|
||||
_isLoading = true;
|
||||
setState(() {});
|
||||
|
||||
int status = await Provider.of<ServiceRequestsProvider>(context).updateServiceReport(report: ServiceReport());
|
||||
_isLoading = false;
|
||||
setState(() {});
|
||||
if (status >= 200 && status < 300) {
|
||||
Fluttertoast.showToast(msg: subtitle.requestCompleteSuccessfully);
|
||||
Navigator.of(context).pop();
|
||||
// Navigator.of(context).pop();
|
||||
} else {
|
||||
String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: subtitle);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(errorMessage)));
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,240 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
|
||||
import 'package:test_sa/models/lookup.dart';
|
||||
import 'package:test_sa/models/service_request/search_work_order.dart';
|
||||
import 'package:test_sa/models/subtitle.dart';
|
||||
import 'package:test_sa/views/widgets/app_text_form_field.dart';
|
||||
import 'package:test_sa/views/widgets/buttons/app_button.dart';
|
||||
import 'package:test_sa/views/widgets/status/report/service_report_maintenance_situation.dart';
|
||||
import 'package:test_sa/views/widgets/status/report/service_report_repair_location.dart';
|
||||
|
||||
import '../../../controllers/api_routes/http_status_manger.dart';
|
||||
import '../../../controllers/localization/localization.dart';
|
||||
import '../../../models/engineer.dart';
|
||||
import '../../../models/service_report.dart';
|
||||
import '../../app_style/sizing.dart';
|
||||
import '../../widgets/date_and_time/time_picker.dart';
|
||||
import '../../widgets/status/report/service_report_all_users.dart';
|
||||
import '../../widgets/titles/app_sub_title.dart';
|
||||
|
||||
class WorkOrderDetailsBottomSheet extends StatefulWidget {
|
||||
final SearchWorkOrders workOrder;
|
||||
const WorkOrderDetailsBottomSheet({this.workOrder, Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<WorkOrderDetailsBottomSheet> createState() => _WorkOrderDetailsBottomSheetState();
|
||||
}
|
||||
|
||||
class _WorkOrderDetailsBottomSheetState extends State<WorkOrderDetailsBottomSheet> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
SearchWorkOrders _workOrder;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_workOrder = widget.workOrder;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
final Subtitle subtitle = AppLocalization.of(context).subtitle;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
topRight: Radius.circular(15),
|
||||
),
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
height: size.height * 0.9,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ASubTitle("WO Details"),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
ATextFormField(enable: false, hintText: "Assigned Employee: ${_workOrder.assignedEmployee?.name}"),
|
||||
const SizedBox(height: 8),
|
||||
const ASubTitle("Assistant Employee"),
|
||||
const SizedBox(height: 4),
|
||||
ServiceReportAllUsers(
|
||||
initialValue: _workOrder.assistantEmployees == null || _workOrder.assistantEmployees.isEmpty
|
||||
? null
|
||||
: Engineer(id: _workOrder.assistantEmployees?.first?.id, name: _workOrder.assistantEmployees?.first?.name),
|
||||
onSelect: (engineer) {
|
||||
_workOrder.assistantEmployees = [AssignedEmployee(id: engineer.id, name: engineer.name)];
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const ASubTitle("Start of Work"),
|
||||
SizedBox(
|
||||
height: 8 * AppStyle.getScaleFactor(context),
|
||||
),
|
||||
ADateTimePicker(
|
||||
date: DateTime.tryParse(_workOrder.startDate ?? ""),
|
||||
from: DateTime.now().subtract(const Duration(days: 365)),
|
||||
to: DateTime.now().add(const Duration(days: 365)),
|
||||
onDateTimePicker: (date) {
|
||||
_workOrder.startDate = date?.toIso8601String();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const ASubTitle("End of Work"),
|
||||
SizedBox(
|
||||
height: 8 * AppStyle.getScaleFactor(context),
|
||||
),
|
||||
ADateTimePicker(
|
||||
date: DateTime.tryParse(_workOrder.endDate ?? ""),
|
||||
from: DateTime.now().subtract(const Duration(days: 365)),
|
||||
to: DateTime.now().add(const Duration(days: 365)),
|
||||
onDateTimePicker: (date) {
|
||||
_workOrder.endDate = date?.toIso8601String();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ASubTitle(subtitle.workingHours),
|
||||
const SizedBox(height: 4),
|
||||
ATextFormField(
|
||||
initialValue: null,
|
||||
textAlign: TextAlign.center,
|
||||
hintText: _workOrder.startDate == null
|
||||
? "0"
|
||||
: ((DateTime.tryParse(_workOrder.endDate)?.difference(DateTime.tryParse(_workOrder.startDate ?? ""))?.inMinutes ?? 0) / 60)?.toStringAsFixed(2)?.toString() ?? "0",
|
||||
enable: false,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
// validator: (value) => Validator.isNumeric(value) ? null : _subtitle.requiredWord,
|
||||
textInputType: TextInputType.number,
|
||||
onSaved: (value) {
|
||||
_workOrder.workingHours = double.tryParse(value);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ATextFormField(
|
||||
labelText: "Travel Hours",
|
||||
initialValue: _workOrder.travelingHours?.toString(),
|
||||
textInputType: TextInputType.number,
|
||||
onSaved: (value) {
|
||||
_workOrder.travelingHours = num.tryParse(value);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ATextFormField(
|
||||
labelText: "Travel Expense",
|
||||
initialValue: _workOrder.travelingExpenses?.toString(),
|
||||
textInputType: TextInputType.number,
|
||||
onSaved: (value) {
|
||||
_workOrder.travelingExpenses = num.tryParse(value);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ASubTitle(subtitle.callLastSituation),
|
||||
const SizedBox(height: 4),
|
||||
ServiceReportMaintenanceSituation(
|
||||
initialValue: _workOrder.calllastSituation,
|
||||
onSelect: (status) {
|
||||
if (status?.value == 12 || _workOrder.calllastSituation?.value == 12) {
|
||||
_workOrder.calllastSituation = status;
|
||||
setState(() {});
|
||||
} else {
|
||||
_workOrder.calllastSituation = status;
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const ASubTitle("Repair Location"),
|
||||
const SizedBox(height: 4),
|
||||
ServiceReportRepairLocation(
|
||||
initialValue: _workOrder.repairLocation != null ? Lookup(name: _workOrder.repairLocation) : null,
|
||||
onSelect: (status) {
|
||||
_workOrder.repairLocation = status.name;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ATextFormField(
|
||||
labelText: "Technical Comments",
|
||||
initialValue: _workOrder.reviewComment,
|
||||
textInputType: TextInputType.multiline,
|
||||
onSaved: (value) {
|
||||
_workOrder.reviewComment = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AButton(
|
||||
text: subtitle.update,
|
||||
onPressed: () async {
|
||||
// _validate = true;
|
||||
if (!_formKey.currentState.validate()) {
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
// if (!_serviceReport.validate()) {
|
||||
// setState(() {});
|
||||
// return;
|
||||
// }
|
||||
_formKey.currentState.save();
|
||||
|
||||
_isLoading = true;
|
||||
setState(() {});
|
||||
|
||||
int status = await Provider.of<ServiceRequestsProvider>(context).updateServiceReport(report: ServiceReport());
|
||||
_isLoading = false;
|
||||
setState(() {});
|
||||
if (status >= 200 && status < 300) {
|
||||
Fluttertoast.showToast(msg: subtitle.requestCompleteSuccessfully);
|
||||
Navigator.of(context).pop();
|
||||
// Navigator.of(context).pop();
|
||||
} else {
|
||||
String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: subtitle);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(errorMessage)));
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue