Browse Source

stamp management

phase2_dev
Đại Võ 1 year ago
parent
commit
dbe5e9611a
30 changed files with 814 additions and 295 deletions
  1. +10
    -0
      android/app/src/main/AndroidManifest.xml
  2. +6
    -0
      android/app/src/main/res/xml/provider_paths.xml
  3. +8
    -0
      android/build.gradle
  4. +7
    -1
      ios/Podfile.lock
  5. +3
    -3
      ios/Runner.xcodeproj/project.pbxproj
  6. +3
    -3
      lib/data/api/dio_provider.dart
  7. +40
    -5
      lib/data/repository/repository.dart
  8. +1
    -1
      lib/presentation/custom_widgets/button/button_2_icon.dart
  9. +262
    -0
      lib/presentation/custom_widgets/dropdown/multiple_select_bottom_sheet.dart
  10. +39
    -4
      lib/presentation/screens/codes/bloc/stamp_bloc.dart
  11. +8
    -18
      lib/presentation/screens/codes/code_detail_page.dart
  12. +48
    -24
      lib/presentation/screens/codes/code_page.dart
  13. +39
    -0
      lib/presentation/screens/codes/create_stamp_page.dart
  14. +35
    -4
      lib/presentation/screens/codes/cubit/create_stamp_cubit.dart
  15. +48
    -6
      lib/presentation/screens/codes/cubit/detail_stamp_cubit.dart
  16. +6
    -2
      lib/presentation/screens/codes/models/stamp.dart
  17. +21
    -0
      lib/presentation/screens/codes/models/stamp_filter_request.dart
  18. +5
    -0
      lib/presentation/screens/codes/models/stamp_request.dart
  19. +3
    -0
      lib/presentation/screens/codes/models/stamp_timeline.dart
  20. +4
    -9
      lib/presentation/screens/codes/widgets/item_code.dart
  21. +1
    -1
      lib/presentation/screens/codes/widgets/item_code_timeline.dart
  22. +36
    -122
      lib/presentation/screens/plot/sc_plot.dart
  23. +22
    -19
      lib/presentation/screens/plot/widget_search.dart
  24. +70
    -0
      lib/presentation/screens/plot/widgets/item_plot.dart
  25. +5
    -48
      lib/presentation/screens/plot_detail/sc_plot_detail.dart
  26. +37
    -24
      lib/presentation/screens/tabbar/tabbar.dart
  27. +13
    -0
      lib/utils/const_common.dart
  28. +15
    -0
      lib/utils/helpers.dart
  29. +16
    -0
      pubspec.lock
  30. +3
    -1
      pubspec.yaml

+ 10
- 0
android/app/src/main/AndroidManifest.xml View File

@@ -60,5 +60,15 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="au.com.homecarenet.caregiver.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"
tools:replace="android:resource" />
</provider>
</application>
</manifest>

+ 6
- 0
android/app/src/main/res/xml/provider_paths.xml View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>

+ 8
- 0
android/build.gradle View File

@@ -25,6 +25,14 @@ subprojects {
}
subprojects {
project.evaluationDependsOn(':app')
project.configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'com.android.support'
&& !details.requested.name.contains('multidex') ) {
details.useVersion "27.1.1"
}
}
}
}

task clean(type: Delete) {

+ 7
- 1
ios/Podfile.lock View File

@@ -120,6 +120,8 @@ PODS:
- nanopb/encode (= 2.30909.0)
- nanopb/decode (2.30909.0)
- nanopb/encode (2.30909.0)
- open_filex (0.0.2):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@@ -151,6 +153,7 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`)
@@ -193,6 +196,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -226,6 +231,7 @@ SPEC CHECKSUMS:
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
@@ -238,4 +244,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: f08360d062df7ffa8ee251115991830e526d3068

COCOAPODS: 1.12.1
COCOAPODS: 1.15.2

+ 3
- 3
ios/Runner.xcodeproj/project.pbxproj View File

@@ -369,7 +369,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 21;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -511,7 +511,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 21;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -547,7 +547,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 21;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (

+ 3
- 3
lib/data/api/dio_provider.dart View File

@@ -78,9 +78,9 @@ class HttpLogInterceptor extends InterceptorsWrapper {

@override
void onResponse(Response<dynamic> response, ResponseInterceptorHandler handler) {
log('🍏🍏🍏🍏🍏🍏 onResponse: ${response.requestOptions.uri}\n'
'statusCode=${response.statusCode}\n'
'data=${response.data is Map ? json.encode(response.data) : response.data}');
// log('🍏🍏🍏🍏🍏🍏 onResponse: ${response.requestOptions.uri}\n'
// 'statusCode=${response.statusCode}\n'
// 'data=${response.data is Map ? json.encode(response.data) : response.data}');
return handler.next(response);
}


+ 40
- 5
lib/data/repository/repository.dart View File

@@ -22,6 +22,7 @@ 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_filter_request.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart';
import 'package:farm_tpf/utils/const_common.dart';
@@ -293,10 +294,17 @@ class Repository {
}

// Stamp
Future<List<Stamp>> stamps({int page = 0, int size = 20}) async {
Future<List<Stamp>> stamps({
int page = 0,
int size = 20,
required StampFilterRequest filter,
}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes/list?page=$page&size=$size&sort=id,desc';
var res = await dio.post(url, data: {});
var url = '${ConstCommon.baseUrl}/api/tb-codes/list?page=$page&size=$size&sort=createdDate,${filter.sort ?? 'asc'}';
var res = await dio.post(url, data: {
'status': filter.status,
'description': filter.description,
});
return (res.data as List).map((e) => Stamp.fromJson(e)).toList();
} catch (e) {
rethrow;
@@ -354,8 +362,13 @@ class Repository {
}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes/create/activity';

await dio.post(url, data: item).then(
var formData = FormData();
formData.fields.add(MapEntry('code', item.code ?? ''));
formData.fields.add(MapEntry('activityTypeId', item.activityTypeId?.toString() ?? ''));
formData.fields.add(MapEntry('executeDate', item.executeDate ?? ''));
formData.fields.add(MapEntry('description', item.description ?? ''));
formData.fields.add(MapEntry('location', item.location ?? ''));
await dio.post(url, data: formData).then(
(value) {
onSuccess(value);
},
@@ -377,6 +390,16 @@ class Repository {
}
}

Future<Stamp> getStampDetailByCode({required String code}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes-scan-qrCode/$code';
var res = await dio.get(url);
return Stamp.fromJson(res.data);
} catch (e) {
rethrow;
}
}

Future<StampTimeline> getStampTimeline({required int stampId}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes-timeline/$stampId';
@@ -407,4 +430,16 @@ class Repository {
onError(AppException.handleError(e));
}
}

Future<List<TbCropDTO>> getPlotsWithoutPaging() async {
try {
var url = '${ConstCommon.baseUrl}/api/_search/tb-crops';
var res = await dio.get(
url,
);
return (res.data as List).map((e) => TbCropDTO.fromJson(e)).toList();
} catch (e) {
rethrow;
}
}
}

+ 1
- 1
lib/presentation/custom_widgets/button/button_2_icon.dart View File

@@ -47,7 +47,7 @@ class Button2Icon extends StatelessWidget {
),
Text(
title,
style: StylesText.body5,
style: StylesText.caption2,
),
const SizedBox(
width: 4,

+ 262
- 0
lib/presentation/custom_widgets/dropdown/multiple_select_bottom_sheet.dart View File

@@ -0,0 +1,262 @@
import 'package:farm_tpf/presentation/custom_widgets/button/second_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:multi_select_flutter/multi_select_flutter.dart';

import '../../../models/item_dropdown.dart';
import '../../../themes/app_colors.dart';
import '../../../themes/styles_text.dart';
import '../button/button_2_icon.dart';

class MultipleSelectBottomSheet extends StatefulWidget {
final List<ItemDropDown>? initValue;
final List<ItemDropDown> dataSources;
final Function(List<ItemDropDown>) onSelected;
final Color? borderColor;
final String hint;
final double? height;
final String? errorText;
final bool isDisable;
final EdgeInsetsGeometry? contentPadding;
final Color? disabledColor;

const MultipleSelectBottomSheet({
Key? key,
required this.dataSources,
required this.onSelected,
required this.hint,
this.initValue,
this.borderColor,
this.height,
this.errorText,
this.isDisable = false,
this.contentPadding,
this.disabledColor,
}) : super(key: key);

@override
_MultipleSelectBottomSheetState createState() => _MultipleSelectBottomSheetState();
}

class _MultipleSelectBottomSheetState extends State<MultipleSelectBottomSheet> {
var dataSources = ValueNotifier(<ItemDropDown>[]);
var selectValue = ValueNotifier(<ItemDropDown>[]);
FixedExtentScrollController? scrollController;
var items = <MultiSelectItem<ItemDropDown>>[];
final _searchCtl = TextEditingController();

@override
void initState() {
super.initState();
}

@override
void dispose() {
super.dispose();
}

@override
Widget build(BuildContext context) {
items = widget.dataSources.map((e) => MultiSelectItem(e, e.value ?? '')).toList();
selectValue.value = widget.initValue ?? <ItemDropDown>[];

return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: selectValue,
builder: (context, selecteds, _) {
return Button2Icon(
leftIcon: CupertinoIcons.slider_horizontal_3,
title: widget.hint,
rightWidget: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.blue,
),
child: Center(
child: Text(
'${selecteds.length}',
style: StylesText.caption6.copyWith(color: Colors.white),
),
),
),
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode());
_showMultiSelect(context);
},
);
},
);
}

void _showMultiSelect(BuildContext context) async {
var selectedIds = selectValue.value.map((e) => e.key).toList();
var selecteds = ValueNotifier(
items
.where((element) => selectedIds.contains(element.value.key))
.map(
(e) => e.value,
)
.toList(),
);
// var selectValue = ValueNotifier(<ItemDropDown>[]);

_searchCtl.clear();
dataSources.value = List.of(widget.dataSources);
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
contentPadding: const EdgeInsets.all(8),
content: Container(
width: double.maxFinite,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildSelectAll(selecteds),
Expanded(
child: ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: dataSources,
builder: (context, sources, _) {
return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: selecteds,
builder: (context, valueSelected, _) {
return ListView.builder(
shrinkWrap: true,
itemBuilder: ((context, index) {
var item = sources[index];
var isSelected = valueSelected.map((e) => e.key ?? '').contains(item.key);
return InkWell(
onTap: () {
if (!isSelected) {
selecteds.value.add(item);
selecteds.notifyListeners();
} else {
selecteds.value.removeWhere((element) => element.key == item.key);
selecteds.notifyListeners();
}
},
child: Row(
children: [
Checkbox(
value: isSelected,
onChanged: (val) {
if (val == true) {
selecteds.value.add(item);
selecteds.notifyListeners();
} else {
selecteds.value.removeWhere((element) => element.key == item.key);
selecteds.notifyListeners();
}
},
),
Expanded(
child: Text(
item.value ?? '',
style: StylesText.body6,
),
),
],
),
);
}),
itemCount: sources.length,
);
});
},
),
),
Row(
children: [
Expanded(
child: SecondButton(
title: 'Huỷ',
color: AppColors.neutral3,
onPressed: () {
Get.back();
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: SecondButton(
title: 'Chọn',
color: AppColors.primary1,
onPressed: () {
Get.back();
selectValue.value = selecteds.value;
widget.onSelected(selectValue.value);
},
),
),
],
)
],
),
),
);
},
);
}

Widget _buildSelectAll(ValueNotifier<List<ItemDropDown>> selecteds) {
return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: dataSources,
builder: (context, sources, _) {
return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: selecteds,
builder: (context, valueSelected, _) {
final isSelectAll = valueSelected.where((e) => sources.any((t) => t.key == e.key)).length == sources.length && sources.isNotEmpty;
final tristate = valueSelected.where((e) => sources.firstWhereOrNull((t) => t.key == e.key) != null).isNotEmpty;
return Visibility(
visible: sources.isNotEmpty,
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppColors.neutral1,
width: 0.5,
),
),
),
child: InkWell(
onTap: () {
if (isSelectAll) {
selecteds.value = List.of(valueSelected)..removeWhere((e) => sources.firstWhereOrNull((t) => t.key == e.key) != null);
} else {
final itemsAdded = List.of(sources)..removeWhere((e) => valueSelected.firstWhereOrNull((t) => t.key == e.key) != null);
selecteds.value = [...valueSelected, ...itemsAdded];
}
},
child: Row(
children: [
Checkbox(
value: isSelectAll ? isSelectAll : (tristate ? null : tristate),
tristate: true,
onChanged: (val) {
if (isSelectAll) {
selecteds.value = List.of(valueSelected)..removeWhere((e) => sources.firstWhereOrNull((t) => t.key == e.key) != null);
} else {
final itemsAdded = List.of(sources)..removeWhere((e) => valueSelected.firstWhereOrNull((t) => t.key == e.key) != null);
selecteds.value = [...valueSelected, ...itemsAdded];
}
},
),
Text(
'Tất cả',
style: StylesText.body6,
),
],
),
),
),
);
},
);
},
);
}
}

+ 39
- 4
lib/presentation/screens/codes/bloc/stamp_bloc.dart View File

@@ -1,8 +1,15 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:farm_tpf/utils/helpers.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../../../../data/api/app_exception.dart';
import '../../../../data/repository/repository.dart';
import '../../../../models/item_dropdown.dart';
import '../../../../utils/const_common.dart';
import '../models/stamp.dart';
import '../models/stamp_filter_request.dart';

part 'stamp_event.dart';
part 'stamp_state.dart';
@@ -11,6 +18,23 @@ class StampBloc extends Bloc<StampEvent, StampState> {
final Repository repository;
int pageSize = 20;
StampBloc(this.repository) : super(StampInitial());
final searchCtl = TextEditingController();
var status = ValueNotifier(
StampStatus.values
.map(
(e) => ItemDropDown(key: describeEnum(e), value: Helpers.getStampStatus(describeEnum(e))),
)
.toList(),
);

var selectedStatus = ValueNotifier(
StampStatus.values
.map(
(e) => ItemDropDown(key: describeEnum(e), value: Helpers.getStampStatus(describeEnum(e))),
)
.toList(),
);
var sort = ValueNotifier(describeEnum(SortType.asc));

@override
Stream<StampState> mapEventToState(
@@ -20,7 +44,7 @@ class StampBloc extends Bloc<StampEvent, StampState> {
try {
if (state is StampInitial) {
yield (StampLoading());
final response = await repository.stamps(page: 0);
final response = await getListStamp(0);
yield StampSuccess(
items: response,
page: 0,
@@ -29,8 +53,11 @@ class StampBloc extends Bloc<StampEvent, StampState> {
}
if (state is StampSuccess) {
final currentState = state as StampSuccess;
if (currentState.hasReachedMax ?? false) {
return;
}
int page = (currentState.page ?? 0) + 1;
final response = await repository.stamps(page: page);
final response = await getListStamp(page);
yield response.isEmpty
? currentState.copyWith(hasReachedMax: true)
: StampSuccess(
@@ -47,7 +74,7 @@ class StampBloc extends Bloc<StampEvent, StampState> {
if (event is OnRefresh) {
try {
yield (StampLoading());
final response = await repository.stamps(page: 0);
final response = await getListStamp(0);
yield StampSuccess(
items: response,
page: 0,
@@ -59,11 +86,19 @@ class StampBloc extends Bloc<StampEvent, StampState> {
} else if (event is OnSearch) {
try {
yield (StampLoading());
final response = await repository.stamps(page: 0);
final response = await getListStamp(0);
yield StampSuccess(items: response, page: 0, hasReachedMax: response.length < pageSize ? true : false);
} catch (e) {
yield (StampFailure(errorString: AppException.handleError(e)));
}
}
}

Future<List<Stamp>> getListStamp(int page) async {
var filter = StampFilterRequest()
..sort = sort.value
..description = searchCtl.text
..status = selectedStatus.value.map((e) => e.key ?? '').toList();
return await repository.stamps(page: 0, filter: filter);
}
}

+ 8
- 18
lib/presentation/screens/codes/code_detail_page.dart View File

@@ -1,9 +1,11 @@
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/bloc/stamp_bloc.dart';
import 'package:farm_tpf/presentation/screens/codes/cubit/detail_stamp_cubit.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.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:farm_tpf/utils/helpers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get/get.dart';
@@ -36,21 +38,7 @@ class _CodeDetailPageState extends State<CodeDetailPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarWidget(
action: IconButton(
onPressed: () {
// Get.to(
// () => UpdateActivityPage(
// stampCode: widget.stampCode,
// ),
// );
},
icon: Icon(
Icons.edit,
color: Colors.blue,
),
),
),
appBar: AppBarWidget(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
@@ -72,7 +60,7 @@ class _CodeDetailPageState extends State<CodeDetailPage> {
children: [
_itemCodeDetail(
title: 'Tên sản phẩm',
detail: 'Cà rốt',
detail: stamp.productName ?? '',
titleStyle: StylesText.body4,
detailStyle: StylesText.body4.copyWith(
color: Colors.blue,
@@ -80,7 +68,7 @@ class _CodeDetailPageState extends State<CodeDetailPage> {
),
_itemCodeDetail(title: 'Mô tả', detail: stamp.description ?? ''),
_itemCodeDetail(title: 'Số lượng tem', detail: stamp.quantity?.formatNumtoStringDecimal().toString() ?? ''),
_itemCodeDetail(title: 'Trạng thái', detail: stamp.status ?? ''),
_itemCodeDetail(title: 'Trạng thái', detail: Helpers.getStampStatus(stamp.status ?? '')),
_itemCodeDetail(title: 'Hạn sử dụng', detail: stamp.expiredDate?.format_DDMMYY().toString() ?? ''),
_itemCodeDetail(title: 'Mẫu tem', detail: stamp.stampType?.exampleStampName ?? ''),
const SizedBox(
@@ -192,7 +180,9 @@ class _CodeDetailPageState extends State<CodeDetailPage> {
),
Expanded(
child: SecondButton(
onPressed: () {},
onPressed: () {
bloc.downloadFile(widget.stampCode);
},
title: 'In / Xuất file',
borderColor: Colors.cyan,
textColor: Colors.white,

+ 48
- 24
lib/presentation/screens/codes/code_page.dart View File

@@ -6,14 +6,19 @@ import 'package:farm_tpf/presentation/screens/codes/create_stamp_page.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart';
import 'package:farm_tpf/presentation/screens/codes/widgets/item_code.dart';
import 'package:farm_tpf/themes/app_colors.dart';
import 'package:farm_tpf/utils/const_common.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get/get.dart';

import '../../../models/item_dropdown.dart';
import '../../../themes/styles_text.dart';
import '../../../utils/const_string.dart';
import '../../../utils/helpers.dart';
import '../../custom_widgets/bottom_loader.dart';
import '../../custom_widgets/dropdown/multiple_select_bottom_sheet.dart';
import '../../custom_widgets/loading_list_page.dart';
import '../plot/widget_search.dart';
import 'bloc/stamp_bloc.dart';
@@ -71,33 +76,52 @@ class _CodePageState extends State<CodePage> {
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22),
),
),
WidgetSearch(),
WidgetSearch(
searchController: bloc.searchCtl,
onPressed: (value) {
Helpers.hideKeyboard(context);
bloc.add(OnRefresh());
},
),
Row(
children: [
Button2Icon(
leftIcon: CupertinoIcons.arrow_up_arrow_down,
rightIcon: CupertinoIcons.chevron_down,
title: 'Sort',
onPressed: () {},
ValueListenableBuilder<String>(
valueListenable: bloc.sort,
builder: (context, sort, _) {
return Button2Icon(
leftIcon: (sort == describeEnum(SortType.asc)) ? CupertinoIcons.arrow_up : CupertinoIcons.arrow_down,
title: 'Ngày tạo',
onPressed: () {
if (sort == describeEnum(SortType.asc)) {
bloc.sort.value = describeEnum(SortType.desc);
} else {
bloc.sort.value = describeEnum(SortType.asc);
}
bloc.sort.notifyListeners();
bloc.add(OnRefresh());
},
);
},
),
Button2Icon(
leftIcon: CupertinoIcons.slider_horizontal_3,
title: 'Filter',
rightWidget: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.blue,
),
child: Center(
child: Text(
'1',
style: StylesText.caption6.copyWith(color: Colors.white),
),
),
),
onPressed: () {},
ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: bloc.selectedStatus,
builder: (context, selecteds, _) {
return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: bloc.status,
builder: (context, status, _) {
return MultipleSelectBottomSheet(
dataSources: status,
initValue: selecteds,
onSelected: (val) {
bloc.selectedStatus.value = val;
Helpers.hideKeyboard(context);
bloc.add(OnRefresh());
},
hint: 'Trạng thái',
);
},
);
},
),
const Spacer(),
SecondButton(

+ 39
- 0
lib/presentation/screens/codes/create_stamp_page.dart View File

@@ -99,6 +99,41 @@ class _CreateStampPageState extends State<CreateStampPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ItemColumnWidget(
title: 'Lô',
child: ValueListenableBuilder<String>(
valueListenable: bloc.selectedPlot,
builder: (context, selected, _) {
return ValueListenableBuilder<List<ItemDropDown>>(
valueListenable: bloc.plots,
builder: (context, plots, _) {
return DropdownBottomSheet(
dataSources: plots,
initValue: selected,
onSelected: (val) {
bloc.selectedPlot.value = val.key ?? '';
},
hint: 'Lô',
);
},
);
},
),
),
const SizedBox(
height: 8,
),
ItemColumnWidget(
title: 'Tên sản phẩm',
child: TextFieldNormal(
controller: bloc.productNameCtl,
maxLines: 1,
hint: 'Tên sản phẩm',
),
),
const SizedBox(
height: 8,
),
ItemColumnWidget(
title: 'Mô tả',
child: TextFieldNormal(
@@ -114,6 +149,10 @@ class _CreateStampPageState extends State<CreateStampPage> {
title: 'Số lượng tem',
child: TextFieldNormal(
controller: bloc.quantityCtl,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
maxLines: 1,
hint: 'Số lượng tem',
),

+ 35
- 4
lib/presentation/screens/codes/cubit/create_stamp_cubit.dart View File

@@ -7,6 +7,7 @@ import 'package:farm_tpf/utils/formatter.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../../../../custom_model/TbCropDTO.dart';
import '../../../../data/api/app_exception.dart';
import '../../../../data/repository/repository.dart';
import '../../../../models/item_dropdown.dart';
@@ -21,15 +22,21 @@ class CreateStampCubit extends Cubit<CreateStampState> {
final descriptionCtl = TextEditingController();
final quantityCtl = TextEditingController();
final expiredDateCtl = TextEditingController();
final productNameCtl = TextEditingController();
var stampTypeRaws = <StampType>[];
var stampTypes = ValueNotifier(<ItemDropDown>[]);
var selectedStampType = ValueNotifier('');

var plotRaws = <TbCropDTO>[];
var plots = ValueNotifier(<ItemDropDown>[]);
var selectedPlot = ValueNotifier('');
// var existedCreateStamp = UpdateCreateStamp();

void dispose() {
descriptionCtl.dispose();
quantityCtl.dispose();
expiredDateCtl.dispose();
productNameCtl.dispose();
}

Future<void> preparedData() async {
@@ -42,6 +49,13 @@ class CreateStampCubit extends Cubit<CreateStampState> {
(e) => ItemDropDown(key: e.id?.toString(), value: e.exampleStampName),
)
.toList();

plotRaws = await repository.getPlotsWithoutPaging();
plots.value = plotRaws
.map(
(e) => ItemDropDown(key: e.id?.toString(), value: e.code),
)
.toList();
emit(CreateStampPrepareDataSuccessful());
} catch (e) {
emit(CreateStampFailure(AppException.handleError(e)));
@@ -51,16 +65,33 @@ class CreateStampCubit extends Cubit<CreateStampState> {
Future<void> onSubmit() async {
if (formKey.currentState!.validate()) {
var requestStamp = RequestStamp();
var stamp = stampTypeRaws.firstWhere(
(e) => selectedStampType.value == e.id?.toString(),
orElse: () => StampType(),
);
var expiredDate = DateTime.now().add(Duration(days: int.tryParse(expiredDateCtl.text) ?? 0));
requestStamp
..tBCropId = int.tryParse(selectedPlot.value)
..productName = productNameCtl.text
..description = descriptionCtl.text
..quantity = int.tryParse(quantityCtl.text) ?? 0
..expiredDate = expiredDate.convertLocalDateTimeToStringUtcDateTime()
..tbExampleStampId = int.tryParse(selectedStampType.value);
if (selectedPlot.value.isEmpty) {
Utils.showSnackBarWarning(message: 'Vui lòng chọn lô');
return;
} else if (descriptionCtl.text.trim().isEmpty) {
Utils.showSnackBarWarning(message: 'Vui lòng nhập mô tả');
return;
} else if (productNameCtl.text.trim().isEmpty) {
Utils.showSnackBarWarning(message: 'Vui lòng nhập tên sản phẩm');
return;
} else if ((int.tryParse(quantityCtl.text) ?? 0) == 0) {
Utils.showSnackBarWarning(message: 'Vui lòng nhập số lượng tem');
return;
} else if ((int.tryParse(expiredDateCtl.text) ?? 0) == 0) {
Utils.showSnackBarWarning(message: 'Vui lòng nhập hạn sử dụng');
return;
} else if (selectedStampType.value.isEmpty) {
Utils.showSnackBarWarning(message: 'Vui lòng nhập mẫu tem');
return;
}
print(requestStamp.toJson());
UtilWidget.showLoading();
await repository.createStamp(

+ 48
- 6
lib/presentation/screens/codes/cubit/detail_stamp_cubit.dart View File

@@ -1,9 +1,17 @@
import 'package:bloc/bloc.dart';
import 'package:dio/dio.dart';
import 'package:equatable/equatable.dart';
import 'package:farm_tpf/presentation/screens/codes/bloc/stamp_bloc.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';

import '../../../../data/api/app_exception.dart';
import '../../../../data/api/dio_provider.dart';
import '../../../../data/repository/repository.dart';
import '../../../../utils/const_common.dart';
import '../../../../utils/utils.dart';
import '../../../custom_widgets/widget_utils.dart';
import '../models/stamp.dart';
@@ -13,6 +21,8 @@ part 'detail_stamp_state.dart';
class DetailStampCubit extends Cubit<DetailStampState> {
final repository = Repository();
DetailStampCubit() : super(DetailStampInitial());
static String pathDownload = 'download';
String currentLocalPath = '';

Future<void> preparedData(int stampId) async {
try {
@@ -20,12 +30,6 @@ class DetailStampCubit extends Cubit<DetailStampState> {
emit(DetailStampLoading());
var stamp = await repository.getStampDetail(id: stampId);
var timeline = await repository.getStampTimeline(stampId: stampId);
// stampTypeRaws = await repository.stampTypes();
// stampTypes.value = stampTypeRaws
// .map(
// (e) => ItemDropDown(key: e.id?.toString(), value: e.exampleStampName),
// )
// .toList();
emit(DetailStampSuccessful(stamp, timeline));
} catch (e) {
emit(DetailStampFailure(AppException.handleError(e)));
@@ -51,4 +55,42 @@ class DetailStampCubit extends Cubit<DetailStampState> {
status: status,
);
}

Future downloadFile(String stampCode) async {
try {
var dio = DioProvider();
print(DateTime.now().millisecondsSinceEpoch);
var fileName = '${DateTime.now().millisecondsSinceEpoch.toString()}.xlsx';

currentLocalPath = await getFilePath(fileName);
UtilWidget.showLoading();
await dio.download(
'${ConstCommon.baseUrl}/api/tb-codes/export?tbCodeValue=$stampCode',
currentLocalPath,
onReceiveProgress: (rec, total) {},
);
UtilWidget.hideDialog();
await onOpenFile();
} catch (e) {
UtilWidget.hideDialog();
Utils.showSnackBarError(message: 'Không thể mở tập tin');
print(e.toString());
}
}

Future<void> onOpenFile() async {
var result = await OpenFilex.open(currentLocalPath);
if (result.type == ResultType.done) {
//can open
} else {
Utils.showSnackBarError(message: 'Không thể mở tập tin');
}
}

Future<String> getFilePath(uniqueFileName) async {
var dir = await getApplicationDocumentsDirectory();
var path = '${dir.path}/$pathDownload/$uniqueFileName';
print(path);
return path;
}
}

+ 6
- 2
lib/presentation/screens/codes/models/stamp.dart View File

@@ -7,6 +7,7 @@ class Stamp {
TbCropDTO? tbCropDTO;
String? code;
num? quantity;
String? productName;
String? description;
String? pathImage;
String? status;
@@ -19,6 +20,7 @@ class Stamp {
this.tbCropDTO,
this.code,
this.quantity,
this.productName,
this.description,
this.pathImage,
this.status,
@@ -29,9 +31,10 @@ class Stamp {

Stamp.fromJson(Map<String, dynamic> json) {
id = json['id'];
tbCropDTO = json['TbCropDTO'] != null ? new TbCropDTO.fromJson(json['TbCropDTO']) : null;
tbCropDTO = json['tbCrop'] != null ? new TbCropDTO.fromJson(json['tbCrop']) : null;
code = json['code'];
quantity = json['quantity'];
productName = json['productName'];
description = json['description'];
pathImage = json['pathImage'];
status = json['status'];
@@ -44,10 +47,11 @@ class Stamp {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
if (this.tbCropDTO != null) {
data['TbCropDTO'] = this.tbCropDTO?.toJson();
data['tbCrop'] = this.tbCropDTO?.toJson();
}
data['code'] = this.code;
data['quantity'] = this.quantity;
data['productName'] = this.productName;
data['description'] = this.description;
data['pathImage'] = this.pathImage;
data['status'] = this.status;

+ 21
- 0
lib/presentation/screens/codes/models/stamp_filter_request.dart View File

@@ -0,0 +1,21 @@
class StampFilterRequest {
List<String>? status;
String? description;
String? sort;

StampFilterRequest({this.status, this.description, this.sort});

StampFilterRequest.fromJson(Map<String, dynamic> json) {
status = json['status'].cast<String>();
description = json['description'];
sort = json['sort'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['description'] = this.description;
data['sort'] = this.sort;
return data;
}
}

+ 5
- 0
lib/presentation/screens/codes/models/stamp_request.dart View File

@@ -4,6 +4,7 @@ class RequestStamp {
int? quantity;
String? description;
String? expiredDate;
String? productName;

RequestStamp({
this.tBCropId,
@@ -11,6 +12,7 @@ class RequestStamp {
this.quantity,
this.description,
this.expiredDate,
this.productName,
});

RequestStamp.fromJson(Map<String, dynamic> json) {
@@ -19,6 +21,7 @@ class RequestStamp {
quantity = json['quantity'];
description = json['description'];
expiredDate = json['expiredDate'];
productName = json['productName'];
}

Map<String, dynamic> toJson() {
@@ -28,6 +31,8 @@ class RequestStamp {
data['quantity'] = this.quantity;
data['description'] = this.description;
data['expiredDate'] = this.expiredDate;
data['productName'] = this.productName;

return data;
}
}

+ 3
- 0
lib/presentation/screens/codes/models/stamp_timeline.dart View File

@@ -27,6 +27,7 @@ class ContentTimeline {
int? cropId;
String? executeDate;
String? description;
String? location;
String? createdDate;
int? createdById;
String? createdByName;
@@ -57,6 +58,7 @@ class ContentTimeline {
cropId = json['cropId'];
executeDate = json['executeDate'];
description = json['description'];
location = json['location'];
createdDate = json['createdDate'];
createdById = json['createdById'];
createdByName = json['createdByName'];
@@ -74,6 +76,7 @@ class ContentTimeline {
data['cropId'] = this.cropId;
data['executeDate'] = this.executeDate;
data['description'] = this.description;
data['location'] = this.location;
data['createdDate'] = this.createdDate;
data['createdById'] = this.createdById;
data['createdByName'] = this.createdByName;

+ 4
- 9
lib/presentation/screens/codes/widgets/item_code.dart View File

@@ -1,4 +1,5 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:farm_tpf/utils/helpers.dart';
import 'package:flutter/material.dart';

import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart';
@@ -20,13 +21,7 @@ class ItemCode extends StatelessWidget {
Widget build(BuildContext context) {
var expiredDate = item.expiredDate?.format_DDMMYY().toString() ?? '';
var quantity = item.quantity;
var status = 'mới';
switch (item.status) {
case 'NEW':
status = 'mới';
break;
default:
}
var status = Helpers.getStampStatus(item.status ?? '');
return GestureDetector(
onTap: () {
onPressed();
@@ -46,8 +41,8 @@ class ItemCode extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.description ?? '',
style: StylesText.body1,
'${item.description ?? ''} - ${item.tbCropDTO?.code ?? ''}',
style: StylesText.body4,
),
const SizedBox(
height: 8,

+ 1
- 1
lib/presentation/screens/codes/widgets/item_code_timeline.dart View File

@@ -92,7 +92,7 @@ class ItemCodeTimeline extends StatelessWidget {
_widgetItemInfoCompleted(
title: 'Mô tả',
actionDate: item.description ?? '',
location: 'dsfsdfsd f sdf dsf sd fds ',
location: item.location ?? '',
),
],
),

+ 36
- 122
lib/presentation/screens/plot/sc_plot.dart View File

@@ -1,18 +1,15 @@
import 'package:farm_tpf/custom_model/TbCropDTO.dart';
import 'package:farm_tpf/data/repository/repository.dart';
import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart';
import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart';
import 'package:farm_tpf/presentation/screens/plot/widget_search.dart';
import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart';
import 'package:farm_tpf/utils/const_assets.dart';
import 'package:farm_tpf/utils/const_color.dart';
import 'package:farm_tpf/utils/pref.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:farm_tpf/utils/const_string.dart';
import 'package:farm_tpf/utils/formatter.dart';

import 'bloc/plot_bloc.dart';
import 'widgets/item_plot.dart';

class PlotListScreen extends StatefulWidget {
@override
@@ -87,7 +84,13 @@ class _InfinityViewState extends State<InfinityView> {
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22),
),
),
WidgetSearch(),
WidgetSearch(
searchController: TextEditingController(),
onPressed: (value) {
FocusScope.of(context).requestFocus(FocusNode());
BlocProvider.of<PlotBloc>(context).add(OnSearch(searchString: value));
},
),
Expanded(child: BlocBuilder<PlotBloc, PlotState>(
builder: (context, state) {
if (state is PlotFailure) {
@@ -98,17 +101,34 @@ class _InfinityViewState extends State<InfinityView> {
return Center(child: Text(label_list_empty));
}
return RefreshIndicator(
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return index >= (state.items ?? []).length ? BottomLoader() : ItemInfinityWidget(item: state.items?[index]);
},
itemCount: (state.hasReachedMax ?? false) ? (state.items ?? []).length : (state.items ?? []).length + 1,
controller: _scrollController,
),
onRefresh: () async {
_plotBloc?.add(OnRefresh());
});
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return index >= (state.items ?? []).length
? BottomLoader()
: ItemPlot(
item: state.items?[index],
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => PlotDetailScreen(
cropId: state.items?[index].id ?? -1,
initialIndex: 0,
cropType: state.items?[index].tbCropTypeId ?? -1,
),
),
);
},
);
},
itemCount: (state.hasReachedMax ?? false) ? (state.items ?? []).length : (state.items ?? []).length + 1,
controller: _scrollController,
),
onRefresh: () async {
_plotBloc?.add(OnRefresh());
},
);
}
return Center(
child: LoadingListPage(),
@@ -125,109 +145,3 @@ class _InfinityViewState extends State<InfinityView> {
super.dispose();
}
}

class ItemInfinityWidget extends StatelessWidget {
final TbCropDTO item;

const ItemInfinityWidget({Key? key, required this.item}) : super(key: key);

@override
Widget build(BuildContext context) {
var backgroundColor;
var textColor;
switch (item.status) {
case "STATUS_ARE_ACTIVE":
backgroundColor = Colors.white;
textColor = AppColors.DEFAULT;
break;
case "STATUS_FINISHED":
backgroundColor = AppColors.DEFAULT;
textColor = Colors.white;
break;
default:
backgroundColor = Colors.white;
textColor = Colors.black;
}

return GestureDetector(
child: Container(
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: backgroundColor,
border: Border.all(color: Colors.grey, width: 0.35),
borderRadius: BorderRadius.circular(8),
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 3, offset: Offset(0, 3))]),
padding: EdgeInsets.all(8),
child: Row(
children: [
Container(
child: Stack(
children: [
Image.asset(
AppAssets.tempImage,
width: 65,
height: 65,
),
Positioned(
child: Container(
color: Colors.grey.withOpacity(0.5),
width: 65,
height: 20,
child: Text(item.areaM2?.formatNumtoStringDecimal().toString() ?? '' + " m\u00B2",
textAlign: TextAlign.center, style: TextStyle(color: Colors.white)),
),
bottom: 0,
)
],
),
),
SizedBox(
width: 12,
),
Expanded(
child: Container(
height: 75,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text("${item.code ?? ''} - ${item.suppliesName ?? ''}", style: TextStyle(color: textColor, fontWeight: FontWeight.bold)),
Expanded(
child: SizedBox(),
),
Row(
children: [
Icon(
Icons.access_time,
size: 16,
color: textColor,
),
SizedBox(
width: 4,
),
Expanded(
child: Text(item.startDate?.format_DDMMYY_HHmm().toString() ?? '', style: TextStyle(color: textColor)),
),
],
)
],
),
),
)
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => PlotDetailScreen(
cropId: item.id ?? -1,
initialIndex: 0,
cropType: item.tbCropTypeId ?? -1,
),
),
);
});
}
}

+ 22
- 19
lib/presentation/screens/plot/widget_search.dart View File

@@ -5,19 +5,24 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/plot_bloc.dart';

class WidgetSearch extends StatefulWidget {
final TextEditingController searchController;
final Function(String) onPressed;

const WidgetSearch({
super.key,
required this.searchController,
required this.onPressed,
});
@override
_WidgetSearchState createState() => _WidgetSearchState();
}

class _WidgetSearchState extends State<WidgetSearch> {
BuildContext? _blocContext;
TextEditingController _searchController = TextEditingController();

@override
void initState() {
super.initState();
_searchController.addListener(() {
final keyword = _searchController.text;
widget.searchController.addListener(() {
final keyword = widget.searchController.text;
if (keyword.isNotEmpty) {
//search when text change
}
@@ -25,7 +30,7 @@ class _WidgetSearchState extends State<WidgetSearch> {
}

Widget getSearchBarUI() {
_searchController.text = "";
// widget.searchController.text = "";
return Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 0, bottom: 4),
child: Row(
@@ -38,24 +43,23 @@ class _WidgetSearchState extends State<WidgetSearch> {
padding: const EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: TextField(
textInputAction: TextInputAction.done,
controller: _searchController,
controller: widget.searchController,
onChanged: (String txt) {},
cursorColor: AppColors.GRAY1,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
Icons.search,
size: 30,
),
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode());
BlocProvider.of<PlotBloc>(_blocContext!).add(OnSearch(searchString: _searchController.text));
}),
icon: Icon(
Icons.search,
size: 30,
),
onPressed: () {
widget.onPressed(widget.searchController.text);
},
),
hintText: 'Tìm theo mã, tên lô',
hintStyle: TextStyle(color: Colors.grey[500])),
onSubmitted: (value) {
FocusScope.of(context).requestFocus(FocusNode());
BlocProvider.of<PlotBloc>(_blocContext!).add(OnSearch(searchString: value));
widget.onPressed(widget.searchController.text);
},
),
),
@@ -69,13 +73,12 @@ class _WidgetSearchState extends State<WidgetSearch> {

@override
Widget build(BuildContext context) {
_blocContext = context;
return Container(child: getSearchBarUI());
}

@override
void dispose() {
_searchController.dispose();
widget.searchController.dispose();
super.dispose();
}
}

+ 70
- 0
lib/presentation/screens/plot/widgets/item_plot.dart View File

@@ -0,0 +1,70 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:farm_tpf/utils/helpers.dart';
import 'package:flutter/material.dart';

import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart';

import '../../../../custom_model/TbCropDTO.dart';
import '../../../../themes/app_colors.dart';
import '../../../../themes/styles_text.dart';
import 'package:farm_tpf/utils/formatter.dart';

class ItemPlot extends StatelessWidget {
final TbCropDTO item;
final Function onPressed;
ItemPlot({
Key? key,
required this.item,
required this.onPressed,
}) : super(key: key);

@override
Widget build(BuildContext context) {
var startDate = item.startDate?.format_DDMMYY().toString() ?? '';
var area = item.areaM2?.formatNumtoStringDecimal().toString() ?? '';
var backgroundColor;
switch (item.status) {
case "STATUS_ARE_ACTIVE":
backgroundColor = Colors.white;
break;
case "STATUS_FINISHED":
backgroundColor = AppColors.primary1;
break;
default:
backgroundColor = Colors.white;
}
return GestureDetector(
onTap: () {
onPressed();
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 8),
margin: EdgeInsets.symmetric(vertical: 4, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
),
color: backgroundColor,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${item.code ?? ''} - ${item.suppliesName ?? ''}",
style: StylesText.body4,
),
const SizedBox(
height: 8,
),
Text(
'$startDate - $area m\u00B2',
style: StylesText.body6,
),
],
),
),
);
}
}

+ 5
- 48
lib/presentation/screens/plot_detail/sc_plot_detail.dart View File

@@ -30,54 +30,11 @@ class _PlotDetailScreenState extends State<PlotDetailScreen> {
String? code,
num? areaM2,
}) {
return Column(
children: [
Container(
padding: EdgeInsets.all(8),
width: double.infinity,
color: Colors.white,
child: Row(
children: [
SizedBox(
width: 12,
),
Expanded(
child: Container(
height: 75,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('${suppliesName ?? ''}', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
Expanded(
child: SizedBox(),
),
Text(
'Mã lô: ${code ?? ''}',
style: TextStyle(color: Colors.grey),
overflow: TextOverflow.clip,
),
Text('Diện tích: ${areaM2?.formatNumtoStringDecimal() ?? '0'} m\u00B2', style: TextStyle(color: Colors.grey))
],
),
),
),
Image.asset(
AppAssets.tempImage,
width: 75,
height: 75,
)
],
),
),
Expanded(
child: HomeTabbarWidget(
cropType: widget.cropType,
cropId: widget.cropId,
cropCode: widget.cropCode,
initialIndex: widget.initialIndex,
))
],
return HomeTabbarWidget(
cropType: widget.cropType,
cropId: widget.cropId,
cropCode: widget.cropCode,
initialIndex: widget.initialIndex,
);
}


+ 37
- 24
lib/presentation/screens/tabbar/tabbar.dart View File

@@ -4,6 +4,7 @@ import 'package:farm_tpf/data/repository/repository.dart';
import 'package:farm_tpf/data/repository/user_repository.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
import 'package:farm_tpf/presentation/screens/account/sc_account.dart';
import 'package:farm_tpf/presentation/screens/codes/code_detail_page.dart';
import 'package:farm_tpf/presentation/screens/codes/code_page.dart';
import 'package:farm_tpf/presentation/screens/control_device/sc_control_device.dart';
import 'package:farm_tpf/presentation/screens/notification/sc_notification.dart';
@@ -303,35 +304,47 @@ class _TabbarScreenState extends State<TabbarScreen> {
)));
}

_showAlertCheckCropCode(String cropCode) async {
_showAlertCheckCropCode(String code) async {
var repository = Repository();
Get.defaultDialog(title: "Kiểm tra thông tin lô ....", middleText: "", content: CircularProgressIndicator());
try {
await repository.getPlotDetailByCode(cropCode).then((value) {
print("ok");
if (Get.isDialogOpen ?? false) Get.back();
Get.to(PlotDetailScreen(cropId: value.tbCropDTO?.id ?? -1, cropType: value.tbCropDTO?.tbCropTypeId ?? -1, initialIndex: 0));
}).catchError((onError) {
Utils.showDialog(
title: "Không tìm thấy lô",
message: "Thử lại với mã tem khác?",
textConfirm: "Thử lại",
textCancel: "Huỷ",
onConfirm: () {
Get.back();
// scan(context);
});
});
if (code.startsWith('LO')) {
await repository.getPlotDetailByCode(code).then((value) {
print("ok");
if (Get.isDialogOpen ?? false) Get.back();
Get.to(
PlotDetailScreen(
cropId: value.tbCropDTO?.id ?? -1,
cropType: value.tbCropDTO?.tbCropTypeId ?? -1,
initialIndex: 0,
),
);
}).catchError((onError) {
Utils.showDialog(
title: "Không tìm thấy lô",
message: "Thử lại với mã tem khác?",
textConfirm: "Thử lại",
textCancel: "Huỷ",
onConfirm: () {
Get.back();
// scan(context);
});
});
} else if (code.startsWith('AC')) {
var stamp = await repository.getStampDetailByCode(code: code);
Get.to(() => CodeDetailPage(stampId: stamp.id ?? 0, stampCode: stamp.code ?? ''));
}
} catch (e) {
Utils.showDialog(
title: "Không tìm thấy lô",
message: "Thử lại với mã tem khác?",
textConfirm: "Thử lại",
textCancel: "Huỷ",
onConfirm: () {
Get.back();
// scan(context);
});
title: "Không tìm thấy lô",
message: "Thử lại với mã tem khác?",
textConfirm: "Thử lại",
textCancel: "Huỷ",
onConfirm: () {
Get.back();
// scan(context);
},
);
}
}
}

+ 13
- 0
lib/utils/const_common.dart View File

@@ -20,4 +20,17 @@ class ConstCommon {
}

enum CRUDStatus { unknown, add, edit, delete }

enum LocationType { country, province, district, ward }

enum StampStatus {
NEW,
ACTIVE,
CANCELED,
EXPIRED,
}

enum SortType {
asc,
desc,
}

+ 15
- 0
lib/utils/helpers.dart View File

@@ -1,3 +1,5 @@
import 'package:farm_tpf/utils/const_common.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class Helpers {
@@ -29,4 +31,17 @@ class Helpers {
return '';
}
}

static String getStampStatus(String status) {
if (status.toUpperCase() == describeEnum(StampStatus.NEW)) {
return 'Mới';
} else if (status.toUpperCase() == describeEnum(StampStatus.ACTIVE)) {
return 'Đã kích hoạt';
} else if (status.toUpperCase() == describeEnum(StampStatus.CANCELED)) {
return 'Đã huỷ';
} else if (status.toUpperCase() == describeEnum(StampStatus.EXPIRED)) {
return 'Hết hạn';
}
return '';
}
}

+ 16
- 0
pubspec.lock View File

@@ -744,6 +744,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0-pre"
multi_select_flutter:
dependency: "direct main"
description:
name: multi_select_flutter
sha256: "503857b415d390d29159df8a9d92d83c6aac17aaf1c307fb7bcfc77d097d20ed"
url: "https://pub.dev"
source: hosted
version: "4.1.3"
nested:
dependency: transitive
description:
@@ -760,6 +768,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
open_filex:
dependency: "direct main"
description:
name: open_filex
sha256: "74e2280754cf8161e860746c3181db2c996d6c1909c7057b738ede4a469816b8"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
package_config:
dependency: transitive
description:

+ 3
- 1
pubspec.yaml View File

@@ -2,7 +2,7 @@ name: farm_tpf
description: A new Flutter project.

publish_to: 'none'
version: 1.1.5+20
version: 1.1.6+21

environment:
sdk: ">=2.17.0 <=3.0.0"
@@ -65,6 +65,8 @@ dependencies:
flutter_switch: ^0.3.2
image_picker: ^0.8.3+2
modal_bottom_sheet: ^3.0.0-pre
open_filex: ^4.4.0
multi_select_flutter: ^4.1.3

dev_dependencies:
flutter_test:

Loading…
Cancel
Save