| @@ -19,6 +19,8 @@ import 'package:farm_tpf/custom_model/user_request.dart'; | |||
| import 'package:farm_tpf/data/api/dio_provider.dart'; | |||
| import 'package:farm_tpf/data/api/rest_client.dart'; | |||
| import 'package:farm_tpf/models/PagedResult.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/activity_request.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/activity_type.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| @@ -331,4 +333,36 @@ class Repository { | |||
| onError(AppException.handleError(e)); | |||
| } | |||
| } | |||
| Future<List<ActivityType>> activityTypes() async { | |||
| try { | |||
| var url = '${ConstCommon.baseUrl}/api/tb-activity-types-dropdown-list-after-harvest'; | |||
| var res = await dio.get( | |||
| url, | |||
| ); | |||
| return (res.data as List).map((e) => ActivityType.fromJson(e)).toList(); | |||
| } catch (e) { | |||
| rethrow; | |||
| } | |||
| } | |||
| Future<void> updateActivity( | |||
| Function(dynamic) onSuccess, | |||
| Function(String) onError, { | |||
| required ActivityRequest item, | |||
| }) async { | |||
| try { | |||
| var url = '${ConstCommon.baseUrl}/api/tb-codes/create/activity'; | |||
| await dio.post(url, data: item).then( | |||
| (value) { | |||
| onSuccess(value); | |||
| }, | |||
| ).catchError((e) { | |||
| onError(AppException.handleError(e)); | |||
| }); | |||
| } catch (e) { | |||
| onError(AppException.handleError(e)); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/button/second_button.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/code_update_timeline_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/update_activity_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/widgets/item_code_timeline.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:get/get.dart'; | |||
| @@ -21,7 +21,9 @@ class _CodeDetailPageState extends State<CodeDetailPage> { | |||
| appBar: AppBarWidget( | |||
| action: IconButton( | |||
| onPressed: () { | |||
| Get.to(() => CodeUpdateTimelinePage()); | |||
| Get.to(() => UpdateActivityPage( | |||
| stampCode: 'AC_494D9D90', | |||
| )); | |||
| }, | |||
| icon: Icon( | |||
| Icons.edit, | |||
| @@ -0,0 +1,83 @@ | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/activity_request.dart'; | |||
| import 'package:farm_tpf/utils/formatter.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:get/get.dart'; | |||
| import '../../../../data/api/app_exception.dart'; | |||
| import '../../../../data/repository/repository.dart'; | |||
| import '../../../../models/item_dropdown.dart'; | |||
| import '../../../../utils/utils.dart'; | |||
| import '../../../custom_widgets/widget_utils.dart'; | |||
| import '../models/activity_type.dart'; | |||
| part 'update_activity_state.dart'; | |||
| class UpdateActivityCubit extends Cubit<UpdateActivityState> { | |||
| UpdateActivityCubit() : super(UpdateActivityInitial()); | |||
| final repository = Repository(); | |||
| final formKey = GlobalKey<FormState>(); | |||
| var actionDate = ValueNotifier(DateTime.now()); | |||
| final descriptionCtl = TextEditingController(); | |||
| final locationCtl = TextEditingController(); | |||
| final expiredDateCtl = TextEditingController(); | |||
| var activityTypeRaws = <ActivityType>[]; | |||
| var activityTypes = ValueNotifier(<ItemDropDown>[]); | |||
| var selectedActivityType = ValueNotifier(''); | |||
| // var existedUpdateActivity = UpdateUpdateActivity(); | |||
| void dispose() { | |||
| descriptionCtl.dispose(); | |||
| locationCtl.dispose(); | |||
| expiredDateCtl.dispose(); | |||
| } | |||
| Future<void> preparedData() async { | |||
| try { | |||
| await Future.delayed(const Duration(seconds: 0)); | |||
| emit(UpdateActivityLoading()); | |||
| activityTypeRaws = await repository.activityTypes(); | |||
| activityTypes.value = activityTypeRaws | |||
| .map( | |||
| (e) => ItemDropDown(key: e.id?.toString(), value: e.description), | |||
| ) | |||
| .toList(); | |||
| emit(UpdateActivityPrepareDataSuccessful()); | |||
| } catch (e) { | |||
| emit(UpdateActivityFailure(AppException.handleError(e))); | |||
| } | |||
| } | |||
| Future<void> onSubmit(String stampCode) async { | |||
| if (formKey.currentState!.validate()) { | |||
| var requestActivity = ActivityRequest(); | |||
| var activity = activityTypeRaws.firstWhere( | |||
| (e) => selectedActivityType.value == e.id?.toString(), | |||
| orElse: () => ActivityType(), | |||
| ); | |||
| requestActivity | |||
| ..code = stampCode | |||
| ..description = descriptionCtl.text | |||
| ..location = locationCtl.text | |||
| ..executeDate = actionDate.value.convertLocalDateTimeToStringUtcDateTime() | |||
| ..activityTypeId = activity.id; | |||
| print(requestActivity.toJson()); | |||
| UtilWidget.showLoading(); | |||
| await repository.updateActivity( | |||
| (success) { | |||
| UtilWidget.hideDialog(); | |||
| Get.back(result: 'ok'); | |||
| Utils.showSnackBarSuccess(); | |||
| }, | |||
| (errorMessage) { | |||
| UtilWidget.hideDialog(); | |||
| Utils.showSnackBarError(); | |||
| }, | |||
| item: requestActivity, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| part of 'update_activity_cubit.dart'; | |||
| class UpdateActivityState extends Equatable { | |||
| const UpdateActivityState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class UpdateActivityInitial extends UpdateActivityState {} | |||
| class UpdateActivityLoading extends UpdateActivityState {} | |||
| class UpdateActivityFailure extends UpdateActivityState { | |||
| final String errorMessage; | |||
| UpdateActivityFailure(this.errorMessage); | |||
| } | |||
| class UpdateActivityPrepareDataSuccessful extends UpdateActivityState { | |||
| UpdateActivityPrepareDataSuccessful(); | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| class ActivityRequest { | |||
| String? code; | |||
| int? activityTypeId; | |||
| String? executeDate; | |||
| String? description; | |||
| String? location; | |||
| ActivityRequest({this.code, this.activityTypeId, this.executeDate, this.description, this.location}); | |||
| ActivityRequest.fromJson(Map<String, dynamic> json) { | |||
| code = json['code']; | |||
| activityTypeId = json['activityTypeId']; | |||
| executeDate = json['executeDate']; | |||
| description = json['description']; | |||
| location = json['location']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['code'] = this.code; | |||
| data['activityTypeId'] = this.activityTypeId; | |||
| data['executeDate'] = this.executeDate; | |||
| data['description'] = this.description; | |||
| data['location'] = this.location; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| class ActivityType { | |||
| int? id; | |||
| String? name; | |||
| String? description; | |||
| ActivityType({this.id, this.name, this.description}); | |||
| ActivityType.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| name = json['name']; | |||
| description = json['description']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['name'] = this.name; | |||
| data['description'] = this.description; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -1,26 +1,28 @@ | |||
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/button_widget.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/cubit/code_update_timeline_cubit.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter/scheduler.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:keyboard_dismisser/keyboard_dismisser.dart'; | |||
| import '../../../models/item_dropdown.dart'; | |||
| import '../../../utils/utils.dart'; | |||
| import '../../custom_widgets/date_picker/date_picker_widget.dart'; | |||
| import '../../custom_widgets/dropdown/dropdown_bottom_sheet.dart'; | |||
| import '../../custom_widgets/textfield/text_field_normal.dart'; | |||
| import 'cubit/update_activity_cubit.dart'; | |||
| import 'widgets/item_column.dart'; | |||
| class CodeUpdateTimelinePage extends StatefulWidget { | |||
| const CodeUpdateTimelinePage({super.key}); | |||
| class UpdateActivityPage extends StatefulWidget { | |||
| final String stampCode; | |||
| const UpdateActivityPage({super.key, required this.stampCode}); | |||
| @override | |||
| State<CodeUpdateTimelinePage> createState() => _CodeUpdateTimelinePageState(); | |||
| State<UpdateActivityPage> createState() => _UpdateActivityPageState(); | |||
| } | |||
| class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||
| final bloc = CodeUpdateTimelineCubit(); | |||
| class _UpdateActivityPageState extends State<UpdateActivityPage> { | |||
| final bloc = UpdateActivityCubit(); | |||
| @override | |||
| void initState() { | |||
| @@ -38,19 +40,19 @@ class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| appBar: AppBarWidget(), | |||
| body: BlocListener<CodeUpdateTimelineCubit, CodeUpdateTimelineState>( | |||
| body: BlocListener<UpdateActivityCubit, UpdateActivityState>( | |||
| bloc: bloc, | |||
| listener: ((context, state) { | |||
| if (state is CodeUpdateTimelineLoading) { | |||
| if (state is UpdateActivityLoading) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.showLoading(); | |||
| }); | |||
| } else if (state is CodeUpdateTimelineFailure) { | |||
| } else if (state is UpdateActivityFailure) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.hideLoading(); | |||
| // UtilWidget.showToastError(state.errorMessage); | |||
| }); | |||
| } else if (state is CodeUpdateTimelinePrepareDataSuccessful) { | |||
| } else if (state is UpdateActivityPrepareDataSuccessful) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.hideLoading(); | |||
| }); | |||
| @@ -69,7 +71,9 @@ class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||
| padding: const EdgeInsets.all(8.0), | |||
| child: ButtonWidget( | |||
| title: 'Cập nhật', | |||
| onPressed: () {}, | |||
| onPressed: () { | |||
| bloc.onSubmit(widget.stampCode); | |||
| }, | |||
| ), | |||
| ), | |||
| ], | |||
| @@ -88,11 +92,45 @@ class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| ItemColumnWidget( | |||
| title: 'Hoạt động', | |||
| child: ValueListenableBuilder<String>( | |||
| valueListenable: bloc.selectedActivityType, | |||
| builder: (context, selected, _) { | |||
| return ValueListenableBuilder<List<ItemDropDown>>( | |||
| valueListenable: bloc.activityTypes, | |||
| builder: (context, types, _) { | |||
| return DropdownBottomSheet( | |||
| dataSources: types, | |||
| initValue: selected, | |||
| onSelected: (val) { | |||
| bloc.selectedActivityType.value = val.key ?? ''; | |||
| }, | |||
| hint: 'Hoạt động', | |||
| ); | |||
| }, | |||
| ); | |||
| }), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Mô tả', | |||
| child: TextFieldNormal( | |||
| controller: bloc.descriptionCtl, | |||
| maxLines: 1, | |||
| hint: 'Mô tả', | |||
| ), | |||
| ), | |||
| SizedBox( | |||
| height: 8, | |||
| ), | |||
| ValueListenableBuilder<DateTime>( | |||
| valueListenable: bloc.actionDate, | |||
| builder: (context, actionDate, _) { | |||
| return ItemColumnWidget( | |||
| title: 'Ngày nhập', | |||
| title: 'Ngày cập nhập', | |||
| child: DatePickerWidget( | |||
| initDateTime: actionDate, | |||
| onUpdateDateTime: (selectedDate) { | |||
| @@ -108,30 +146,11 @@ class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Hoạt động', | |||
| child: ValueListenableBuilder<String>( | |||
| valueListenable: bloc.selectedActionType, | |||
| builder: (context, selected, _) { | |||
| return DropdownBottomSheet( | |||
| dataSources: bloc.actionTypes, | |||
| initValue: selected, | |||
| onSelected: (val) { | |||
| bloc.selectedActionType.value = val.key ?? ''; | |||
| }, | |||
| hint: 'Hoạt động', | |||
| ); | |||
| }, | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Mô tả', | |||
| title: 'Vị trí', | |||
| child: TextFieldNormal( | |||
| controller: bloc.descriptionCtl, | |||
| maxLines: 2, | |||
| hint: 'Mô tả', | |||
| controller: bloc.locationCtl, | |||
| maxLines: 1, | |||
| hint: 'Vị trí', | |||
| ), | |||
| ), | |||
| const SizedBox( | |||