| @@ -19,11 +19,15 @@ 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/stamp.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import '../../presentation/screens/codes/models/stamp_type.dart'; | |||
| import '../../presentation/screens/login/models/request_user.dart'; | |||
| import '../../presentation/screens/login/models/response_user.dart'; | |||
| import '../api/app_exception.dart'; | |||
| class Repository { | |||
| final dio = DioProvider(); | |||
| @@ -284,4 +288,47 @@ class Repository { | |||
| final client = RestClient(dio); | |||
| return client.getDetailActivityCommon(activityType, activityId); | |||
| } | |||
| // Stamp | |||
| Future<List<Stamp>> stamps({int page = 0, int size = 20}) async { | |||
| try { | |||
| var url = '${ConstCommon.baseUrl}/api/tb-codes/list?page=$page&size=$size&sort=id,desc'; | |||
| var res = await dio.post(url, data: {}); | |||
| return (res.data as List).map((e) => Stamp.fromJson(e)).toList(); | |||
| } catch (e) { | |||
| rethrow; | |||
| } | |||
| } | |||
| Future<List<StampType>> stampTypes() async { | |||
| try { | |||
| var url = '${ConstCommon.baseUrl}/api/tb-example-stamp/dropdown-list'; | |||
| var res = await dio.get( | |||
| url, | |||
| ); | |||
| return (res.data as List).map((e) => StampType.fromJson(e)).toList(); | |||
| } catch (e) { | |||
| rethrow; | |||
| } | |||
| } | |||
| Future<void> createStamp( | |||
| Function(dynamic) onSuccess, | |||
| Function(String) onError, { | |||
| required RequestStamp item, | |||
| }) async { | |||
| try { | |||
| var url = '${ConstCommon.baseUrl}/api/tb-codes'; | |||
| await dio.post(url, data: item).then( | |||
| (value) { | |||
| onSuccess(value); | |||
| }, | |||
| ).catchError((e) { | |||
| onError(AppException.handleError(e)); | |||
| }); | |||
| } catch (e) { | |||
| onError(AppException.handleError(e)); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,9 +1,12 @@ | |||
| // import 'package:barcode_scan/barcode_scan.dart'; | |||
| import 'dart:io'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/bloc/stamp_bloc.dart'; | |||
| import 'package:firebase_core/firebase_core.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter/services.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:hive/hive.dart'; | |||
| import 'package:path_provider/path_provider.dart'; | |||
| import 'app.dart'; | |||
| @@ -34,7 +37,18 @@ Future<void> main() async { | |||
| final appDocumentDirectory = await getApplicationDocumentsDirectory(); | |||
| Hive..init(appDocumentDirectory.path); | |||
| await Hive.openBox('LocalDBName'); | |||
| runApp(App( | |||
| authenticationRepository: AuthenticationRepository(), | |||
| )); | |||
| runApp( | |||
| MultiBlocProvider( | |||
| providers: [ | |||
| BlocProvider( | |||
| create: (_) => StampBloc( | |||
| Repository(), | |||
| ), | |||
| ), | |||
| ], | |||
| child: App( | |||
| authenticationRepository: AuthenticationRepository(), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import '../../../../data/api/app_exception.dart'; | |||
| import '../../../../data/repository/repository.dart'; | |||
| part 'stamp_event.dart'; | |||
| part 'stamp_state.dart'; | |||
| class StampBloc extends Bloc<StampEvent, StampState> { | |||
| final Repository repository; | |||
| int pageSize = 20; | |||
| StampBloc(this.repository) : super(StampInitial()); | |||
| @override | |||
| Stream<StampState> mapEventToState( | |||
| StampEvent event, | |||
| ) async* { | |||
| if (event is DataFetched && !(state is StampSuccess && ((state as StampSuccess).hasReachedMax ?? false))) { | |||
| try { | |||
| if (state is StampInitial) { | |||
| yield (StampLoading()); | |||
| final response = await repository.stamps(page: 0); | |||
| yield StampSuccess( | |||
| items: response, | |||
| page: 0, | |||
| hasReachedMax: response.length < pageSize ? true : false, | |||
| ); | |||
| } | |||
| if (state is StampSuccess) { | |||
| final currentState = state as StampSuccess; | |||
| int page = (currentState.page ?? 0) + 1; | |||
| final response = await repository.stamps(page: page); | |||
| yield response.isEmpty | |||
| ? currentState.copyWith(hasReachedMax: true) | |||
| : StampSuccess( | |||
| items: (currentState.items ?? []) + response, | |||
| page: (currentState.page ?? 0) + 1, | |||
| hasReachedMax: false, | |||
| ); | |||
| } | |||
| } catch (e) { | |||
| var errorString = AppException.handleError(e); | |||
| yield (StampFailure(errorString: errorString)); | |||
| } | |||
| } | |||
| if (event is OnRefresh) { | |||
| try { | |||
| yield (StampLoading()); | |||
| final response = await repository.stamps(page: 0); | |||
| yield StampSuccess( | |||
| items: response, | |||
| page: 0, | |||
| hasReachedMax: response.length < pageSize ? true : false, | |||
| ); | |||
| } catch (e) { | |||
| yield (StampFailure(errorString: AppException.handleError(e))); | |||
| } | |||
| } else if (event is OnSearch) { | |||
| try { | |||
| yield (StampLoading()); | |||
| final response = await repository.stamps(page: 0); | |||
| yield StampSuccess(items: response, page: 0, hasReachedMax: response.length < pageSize ? true : false); | |||
| } catch (e) { | |||
| yield (StampFailure(errorString: AppException.handleError(e))); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| part of 'stamp_bloc.dart'; | |||
| abstract class StampEvent extends Equatable { | |||
| const StampEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class DataFetched extends StampEvent {} | |||
| class OnRefresh extends StampEvent {} | |||
| class OnSearch extends StampEvent { | |||
| final String searchString; | |||
| OnSearch({required this.searchString}); | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| part of 'stamp_bloc.dart'; | |||
| abstract class StampState extends Equatable { | |||
| const StampState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class StampInitial extends StampState {} | |||
| class StampLoading extends StampState {} | |||
| class StampFailure extends StampState { | |||
| final String errorString; | |||
| StampFailure({required this.errorString}); | |||
| } | |||
| class StampSuccess<T> extends StampState { | |||
| final List<T>? items; | |||
| final int? page; | |||
| final bool? hasReachedMax; | |||
| const StampSuccess({this.items, this.page, this.hasReachedMax}); | |||
| StampSuccess copyWith({List<T>? items, int? page, bool? hasReachedMax}) { | |||
| return StampSuccess( | |||
| items: items ?? this.items, | |||
| page: page ?? this.page, | |||
| hasReachedMax: hasReachedMax ?? this.hasReachedMax, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,14 +1,22 @@ | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/button/button_2_icon.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/button/second_button.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/code_detail_page.dart'; | |||
| 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:flutter/cupertino.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:get/get.dart'; | |||
| import '../../../themes/styles_text.dart'; | |||
| import '../../../utils/const_string.dart'; | |||
| import '../../custom_widgets/bottom_loader.dart'; | |||
| import '../../custom_widgets/loading_list_page.dart'; | |||
| import '../plot/widget_search.dart'; | |||
| import 'bloc/stamp_bloc.dart'; | |||
| class CodePage extends StatefulWidget { | |||
| const CodePage({super.key}); | |||
| @@ -19,6 +27,30 @@ class CodePage extends StatefulWidget { | |||
| class _CodePageState extends State<CodePage> { | |||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||
| StampBloc bloc = StampBloc(Repository()); | |||
| final _scrollController = ScrollController(); | |||
| final _scrollThreshold = 250.0; | |||
| @override | |||
| void initState() { | |||
| bloc.add(DataFetched()); | |||
| _scrollController.addListener(() { | |||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||
| final currentScroll = _scrollController.position.pixels; | |||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||
| bloc.add(DataFetched()); | |||
| } | |||
| }); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| _scrollController.dispose(); | |||
| super.dispose(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| @@ -69,7 +101,13 @@ class _CodePageState extends State<CodePage> { | |||
| ), | |||
| const Spacer(), | |||
| SecondButton( | |||
| onPressed: () {}, | |||
| onPressed: () { | |||
| Get.to(() => CreateStampPage())?.then((value) { | |||
| if (value != null) { | |||
| bloc.add(OnRefresh()); | |||
| } | |||
| }); | |||
| }, | |||
| title: 'Tạo tem', | |||
| leftIcon: CupertinoIcons.add, | |||
| color: AppColors.primary1, | |||
| @@ -79,20 +117,49 @@ class _CodePageState extends State<CodePage> { | |||
| ], | |||
| ), | |||
| Expanded( | |||
| child: ListView.builder( | |||
| itemBuilder: ((context, index) { | |||
| return ItemCode( | |||
| onPressed: () { | |||
| Get.to(() => CodeDetailPage()); | |||
| }, | |||
| ); | |||
| }), | |||
| itemCount: 100, | |||
| ), | |||
| child: mainBody(), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| Widget mainBody() { | |||
| return BlocBuilder<StampBloc, StampState>( | |||
| bloc: bloc, | |||
| builder: (context, state) { | |||
| if (state is StampFailure) { | |||
| return Center(child: Text(state.errorString)); | |||
| } | |||
| if (state is StampSuccess) { | |||
| if ((state.items ?? []).isEmpty) { | |||
| 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() | |||
| : ItemCode( | |||
| item: state.items?[index], | |||
| onPressed: () { | |||
| Get.to(() => CodeDetailPage()); | |||
| }, | |||
| ); | |||
| }, | |||
| itemCount: (state.hasReachedMax ?? false) ? (state.items ?? []).length : (state.items ?? []).length + 1, | |||
| controller: _scrollController, | |||
| ), | |||
| onRefresh: () async { | |||
| bloc.add(OnRefresh()); | |||
| }); | |||
| } | |||
| return Center( | |||
| child: LoadingListPage(), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,201 @@ | |||
| import 'package:cached_network_image/cached_network_image.dart'; | |||
| import 'package:farm_tpf/models/item_dropdown.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_type.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/widgets/item_column.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter/scheduler.dart'; | |||
| import 'package:flutter/services.dart'; | |||
| import 'package:flutter/src/widgets/framework.dart'; | |||
| import 'package:flutter/src/widgets/placeholder.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:get/get.dart'; | |||
| import 'package:keyboard_dismisser/keyboard_dismisser.dart'; | |||
| import '../../../utils/utils.dart'; | |||
| import '../../custom_widgets/app_bar_widget.dart'; | |||
| import '../../custom_widgets/button_widget.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/create_stamp_cubit.dart'; | |||
| class CreateStampPage extends StatefulWidget { | |||
| const CreateStampPage({ | |||
| super.key, | |||
| }); | |||
| @override | |||
| State<CreateStampPage> createState() => _CreateStampPageState(); | |||
| } | |||
| class _CreateStampPageState extends State<CreateStampPage> { | |||
| final bloc = CreateStampCubit(); | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| bloc.preparedData(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| bloc.dispose(); | |||
| super.dispose(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| appBar: AppBarWidget(), | |||
| body: BlocListener<CreateStampCubit, CreateStampState>( | |||
| bloc: bloc, | |||
| listener: ((context, state) { | |||
| if (state is CreateStampLoading) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.showLoading(); | |||
| }); | |||
| } else if (state is CreateStampFailure) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.hideLoading(); | |||
| // UtilWidget.showToastError(state.errorMessage); | |||
| }); | |||
| } else if (state is CreateStampPrepareDataSuccessful) { | |||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||
| UtilWidget.hideLoading(); | |||
| }); | |||
| } | |||
| }), | |||
| child: KeyboardDismisser( | |||
| child: Container( | |||
| child: Form( | |||
| key: bloc.formKey, | |||
| child: Column( | |||
| children: [ | |||
| Expanded( | |||
| child: _widgetBody(), | |||
| ), | |||
| Padding( | |||
| padding: const EdgeInsets.all(8.0), | |||
| child: ButtonWidget( | |||
| title: 'Cập nhật', | |||
| onPressed: () { | |||
| bloc.onSubmit(); | |||
| }, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| Widget _widgetBody() { | |||
| return Container( | |||
| padding: const EdgeInsets.all(16), | |||
| child: SingleChildScrollView( | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| ItemColumnWidget( | |||
| title: 'Mô tả', | |||
| child: TextFieldNormal( | |||
| controller: bloc.descriptionCtl, | |||
| maxLines: 1, | |||
| hint: 'Mô tả', | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Số lượng tem', | |||
| child: TextFieldNormal( | |||
| controller: bloc.quantityCtl, | |||
| maxLines: 1, | |||
| hint: 'Số lượng tem', | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Hạn sử dụng (ngày)', | |||
| child: TextFieldNormal( | |||
| controller: bloc.expiredDateCtl, | |||
| maxLines: 1, | |||
| keyboardType: TextInputType.number, | |||
| inputFormatters: [ | |||
| FilteringTextInputFormatter.digitsOnly, | |||
| ], | |||
| hint: 'Hạn sử dụng (ngày)', | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ItemColumnWidget( | |||
| title: 'Mẫu tem', | |||
| child: ValueListenableBuilder<String>( | |||
| valueListenable: bloc.selectedStampType, | |||
| builder: (context, selected, _) { | |||
| return ValueListenableBuilder<List<ItemDropDown>>( | |||
| valueListenable: bloc.stampTypes, | |||
| builder: (context, types, _) { | |||
| return DropdownBottomSheet( | |||
| dataSources: types, | |||
| initValue: selected, | |||
| onSelected: (val) { | |||
| bloc.selectedStampType.value = val.key ?? ''; | |||
| }, | |||
| hint: 'Mẫu tem', | |||
| ); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| ValueListenableBuilder<String>( | |||
| valueListenable: bloc.selectedStampType, | |||
| builder: (context, selected, _) { | |||
| if (selected.isEmpty) { | |||
| return const SizedBox.shrink(); | |||
| } | |||
| var stamp = bloc.stampTypeRaws.firstWhere( | |||
| (e) => selected == e.id?.toString(), | |||
| orElse: () => StampType(), | |||
| ); | |||
| return Container( | |||
| child: CachedNetworkImage( | |||
| imageUrl: stamp.exampleStampImage ?? '', | |||
| width: Get.width, | |||
| height: Get.width / 2, | |||
| fit: BoxFit.contain, | |||
| placeholder: (context, url) => Icon( | |||
| Icons.image, | |||
| size: 100, | |||
| color: Colors.grey[200], | |||
| ), | |||
| errorWidget: (context, url, _) => Icon( | |||
| Icons.image_not_supported, | |||
| size: 100, | |||
| color: Colors.grey[200], | |||
| ), | |||
| ), | |||
| ); | |||
| }, | |||
| ), | |||
| const SizedBox( | |||
| height: 16, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,80 @@ | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_type.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'; | |||
| part 'create_stamp_state.dart'; | |||
| class CreateStampCubit extends Cubit<CreateStampState> { | |||
| CreateStampCubit() : super(CreateStampInitial()); | |||
| final repository = Repository(); | |||
| final formKey = GlobalKey<FormState>(); | |||
| final descriptionCtl = TextEditingController(); | |||
| final quantityCtl = TextEditingController(); | |||
| final expiredDateCtl = TextEditingController(); | |||
| var stampTypeRaws = <StampType>[]; | |||
| var stampTypes = ValueNotifier(<ItemDropDown>[]); | |||
| var selectedStampType = ValueNotifier(''); | |||
| // var existedCreateStamp = UpdateCreateStamp(); | |||
| void dispose() { | |||
| descriptionCtl.dispose(); | |||
| quantityCtl.dispose(); | |||
| expiredDateCtl.dispose(); | |||
| } | |||
| Future<void> preparedData() async { | |||
| try { | |||
| await Future.delayed(const Duration(seconds: 0)); | |||
| emit(CreateStampLoading()); | |||
| stampTypeRaws = await repository.stampTypes(); | |||
| stampTypes.value = stampTypeRaws | |||
| .map( | |||
| (e) => ItemDropDown(key: e.id?.toString(), value: e.exampleStampName), | |||
| ) | |||
| .toList(); | |||
| emit(CreateStampPrepareDataSuccessful()); | |||
| } catch (e) { | |||
| emit(CreateStampFailure(AppException.handleError(e))); | |||
| } | |||
| } | |||
| 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 | |||
| ..description = descriptionCtl.text | |||
| ..quantity = int.tryParse(quantityCtl.text) ?? 0 | |||
| ..expiredDate = expiredDate.convertLocalDateTimeToStringUtcDateTime() | |||
| ..tbExampleStampId = int.tryParse(selectedStampType.value); | |||
| print(requestStamp.toJson()); | |||
| UtilWidget.showLoading(); | |||
| await repository.createStamp( | |||
| (success) { | |||
| UtilWidget.hideDialog(); | |||
| Get.back(result: 'ok'); | |||
| Utils.showSnackBarSuccess(); | |||
| }, | |||
| (errorMessage) { | |||
| UtilWidget.hideDialog(); | |||
| Utils.showSnackBarError(); | |||
| }, | |||
| item: requestStamp, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| part of 'create_stamp_cubit.dart'; | |||
| class CreateStampState extends Equatable { | |||
| const CreateStampState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class CreateStampInitial extends CreateStampState {} | |||
| class CreateStampLoading extends CreateStampState {} | |||
| class CreateStampFailure extends CreateStampState { | |||
| final String errorMessage; | |||
| CreateStampFailure(this.errorMessage); | |||
| } | |||
| class CreateStampPrepareDataSuccessful extends CreateStampState { | |||
| CreateStampPrepareDataSuccessful(); | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| import '../../../../custom_model/TbCropDTO.dart'; | |||
| class Stamp { | |||
| int? id; | |||
| TbCropDTO? tbCropDTO; | |||
| String? code; | |||
| num? quantity; | |||
| String? description; | |||
| String? pathImage; | |||
| String? status; | |||
| String? expiredDate; | |||
| String? createdDate; | |||
| Stamp({ | |||
| this.id, | |||
| this.tbCropDTO, | |||
| this.code, | |||
| this.quantity, | |||
| this.description, | |||
| this.pathImage, | |||
| this.status, | |||
| this.expiredDate, | |||
| this.createdDate, | |||
| }); | |||
| Stamp.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| tbCropDTO = json['TbCropDTO'] != null ? new TbCropDTO.fromJson(json['TbCropDTO']) : null; | |||
| code = json['code']; | |||
| quantity = json['quantity']; | |||
| description = json['description']; | |||
| pathImage = json['pathImage']; | |||
| status = json['status']; | |||
| expiredDate = json['expiredDate']; | |||
| createdDate = json['createdDate']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| if (this.tbCropDTO != null) { | |||
| data['TbCropDTO'] = this.tbCropDTO?.toJson(); | |||
| } | |||
| data['code'] = this.code; | |||
| data['quantity'] = this.quantity; | |||
| data['description'] = this.description; | |||
| data['pathImage'] = this.pathImage; | |||
| data['status'] = this.status; | |||
| data['expiredDate'] = this.expiredDate; | |||
| data['createdDate'] = this.createdDate; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| class RequestStamp { | |||
| int? tBCropId; | |||
| int? tbExampleStampId; | |||
| int? quantity; | |||
| String? description; | |||
| String? expiredDate; | |||
| RequestStamp({ | |||
| this.tBCropId, | |||
| this.tbExampleStampId, | |||
| this.quantity, | |||
| this.description, | |||
| this.expiredDate, | |||
| }); | |||
| RequestStamp.fromJson(Map<String, dynamic> json) { | |||
| tBCropId = json['tBCropId']; | |||
| tbExampleStampId = json['tbExampleStampId']; | |||
| quantity = json['quantity']; | |||
| description = json['description']; | |||
| expiredDate = json['expiredDate']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['tBCropId'] = this.tBCropId; | |||
| data['tbExampleStampId'] = this.tbExampleStampId; | |||
| data['quantity'] = this.quantity; | |||
| data['description'] = this.description; | |||
| data['expiredDate'] = this.expiredDate; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| class StampType { | |||
| int? id; | |||
| String? exampleStampName; | |||
| String? exampleStampImage; | |||
| StampType({this.id, this.exampleStampName, this.exampleStampImage}); | |||
| StampType.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| exampleStampName = json['exampleStampName']; | |||
| exampleStampImage = json['exampleStampImage']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['exampleStampName'] = this.exampleStampName; | |||
| data['exampleStampImage'] = this.exampleStampImage; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -1,17 +1,32 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart'; | |||
| import '../../../../themes/app_colors.dart'; | |||
| import '../../../../themes/styles_text.dart'; | |||
| import 'package:farm_tpf/utils/formatter.dart'; | |||
| class ItemCode extends StatelessWidget { | |||
| final Stamp item; | |||
| final Function onPressed; | |||
| const ItemCode({ | |||
| super.key, | |||
| ItemCode({ | |||
| Key? key, | |||
| required this.item, | |||
| required this.onPressed, | |||
| }); | |||
| }) : super(key: key); | |||
| @override | |||
| 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: | |||
| } | |||
| return GestureDetector( | |||
| onTap: () { | |||
| onPressed(); | |||
| @@ -31,14 +46,14 @@ class ItemCode extends StatelessWidget { | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| Text( | |||
| 'Cà chua đợt 1 5/2023', | |||
| item.description ?? '', | |||
| style: StylesText.body1, | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| Text( | |||
| '06/05/2023 - 1500 tem - mới', | |||
| '$expiredDate - $quantity tem - $status', | |||
| style: StylesText.body6, | |||
| ), | |||
| ], | |||
| @@ -42,8 +42,8 @@ class _TabbarScreenState extends State<TabbarScreen> { | |||
| final changeTabbar = Get.put(TabbarSelected()); | |||
| List<TabbarItem> itemsTabbar = [ | |||
| TabbarItem(icon: AppIcons.icPlot, title: 'Lô trồng', index: TabBarIndex.plot), | |||
| TabbarItem(icon: AppIcons.icDevice, title: 'Thiết bị', index: TabBarIndex.device), | |||
| // TabbarItem(icon: AppIcons.icQrManage, title: 'Quản lý QR', index: TabBarIndex.qrManage), | |||
| // TabbarItem(icon: AppIcons.icDevice, title: 'Thiết bị', index: TabBarIndex.device), | |||
| TabbarItem(icon: AppIcons.icQrManage, title: 'Quản lý QR', index: TabBarIndex.qrManage), | |||
| TabbarItem(icon: AppIcons.icQr, title: 'Quét QR', index: TabBarIndex.qr), | |||
| TabbarItem(icon: AppIcons.icNotification, title: 'Thông báo', index: TabBarIndex.notification), | |||
| TabbarItem(icon: AppIcons.icPerson, title: 'Cá nhân', index: TabBarIndex.account) | |||
| @@ -209,12 +209,12 @@ class _TabbarScreenState extends State<TabbarScreen> { | |||
| case TabBarIndex.plot: | |||
| return PlotListScreen(); | |||
| break; | |||
| case TabBarIndex.device: | |||
| return ControlDeviceScreen(); | |||
| break; | |||
| // case TabBarIndex.qrManage: | |||
| // return CodePage(); | |||
| // case TabBarIndex.device: | |||
| // return ControlDeviceScreen(); | |||
| // break; | |||
| case TabBarIndex.qrManage: | |||
| return CodePage(); | |||
| break; | |||
| case TabBarIndex.qr: | |||
| return Container(); | |||
| break; | |||
| @@ -351,8 +351,8 @@ class TabbarSelected extends GetxController { | |||
| enum TabBarIndex { | |||
| plot, | |||
| device, | |||
| // qrManage, | |||
| // device, | |||
| qrManage, | |||
| qr, | |||
| notification, | |||
| account, | |||
| @@ -45,6 +45,16 @@ extension ddMM_HHmm on String { | |||
| } | |||
| } | |||
| String format_DDMMYY() { | |||
| try { | |||
| final str = this.toString(); | |||
| var dateFromString = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(str, true).toLocal(); | |||
| return DateFormat("dd/MM/yyyy").format(dateFromString); | |||
| } catch (_) { | |||
| return ""; | |||
| } | |||
| } | |||
| DateTime convertStringServerDateTimeToLocalDateTime() { | |||
| try { | |||
| return DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(this, true).toLocal(); | |||