Browse Source

detail stamp, timeline, update stamp status

bugfix/20250501
Đại Võ 1 year ago
parent
commit
1790f593f5
12 changed files with 339 additions and 154 deletions
  1. +42
    -0
      lib/data/repository/repository.dart
  2. +4
    -0
      lib/main.dart
  3. +3
    -3
      lib/presentation/screens/codes/bloc/stamp_state.dart
  4. +95
    -38
      lib/presentation/screens/codes/code_detail_page.dart
  5. +6
    -1
      lib/presentation/screens/codes/code_page.dart
  6. +0
    -67
      lib/presentation/screens/codes/cubit/code_update_timeline_cubit.dart
  7. +0
    -22
      lib/presentation/screens/codes/cubit/code_update_timeline_state.dart
  8. +54
    -0
      lib/presentation/screens/codes/cubit/detail_stamp_cubit.dart
  9. +24
    -0
      lib/presentation/screens/codes/cubit/detail_stamp_state.dart
  10. +8
    -0
      lib/presentation/screens/codes/models/stamp.dart
  11. +87
    -0
      lib/presentation/screens/codes/models/stamp_timeline.dart
  12. +16
    -23
      lib/presentation/screens/codes/widgets/item_code_timeline.dart

+ 42
- 0
lib/data/repository/repository.dart View File

import 'package:farm_tpf/presentation/screens/codes/models/activity_type.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.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_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'; import 'package:farm_tpf/utils/const_common.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';


onError(AppException.handleError(e)); onError(AppException.handleError(e));
} }
} }

Future<Stamp> getStampDetail({required int id}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes/$id';
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';
var res = await dio.get(url);
return StampTimeline.fromJson(res.data);
} catch (e) {
rethrow;
}
}

Future<void> updateStampStatus(
Function(dynamic) onSuccess,
Function(String) onError, {
required int stampId,
required String status,
}) async {
try {
var url = '${ConstCommon.baseUrl}/api/tb-codes/update/$stampId/status/$status';

await dio.put(url).then(
(value) {
onSuccess(value);
},
).catchError((e) {
onError(AppException.handleError(e));
});
} catch (e) {
onError(AppException.handleError(e));
}
}
} }

+ 4
- 0
lib/main.dart View File



import 'package:farm_tpf/data/repository/repository.dart'; import 'package:farm_tpf/data/repository/repository.dart';
import 'package:farm_tpf/presentation/screens/codes/bloc/stamp_bloc.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:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
Repository(), Repository(),
), ),
), ),
BlocProvider(
create: (_) => DetailStampCubit(),
),
], ],
child: App( child: App(
authenticationRepository: AuthenticationRepository(), authenticationRepository: AuthenticationRepository(),

+ 3
- 3
lib/presentation/screens/codes/bloc/stamp_state.dart View File

StampFailure({required this.errorString}); StampFailure({required this.errorString});
} }


class StampSuccess<T> extends StampState {
final List<T>? items;
class StampSuccess<Stamp> extends StampState {
final List<Stamp>? items;
final int? page; final int? page;
final bool? hasReachedMax; final bool? hasReachedMax;


const StampSuccess({this.items, this.page, this.hasReachedMax}); const StampSuccess({this.items, this.page, this.hasReachedMax});


StampSuccess copyWith({List<T>? items, int? page, bool? hasReachedMax}) {
StampSuccess copyWith({List<Stamp>? items, int? page, bool? hasReachedMax}) {
return StampSuccess( return StampSuccess(
items: items ?? this.items, items: items ?? this.items,
page: page ?? this.page, page: page ?? this.page,

+ 95
- 38
lib/presentation/screens/codes/code_detail_page.dart View File

import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; 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/custom_widgets/button/second_button.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/update_activity_page.dart';
import 'package:farm_tpf/presentation/screens/codes/widgets/item_code_timeline.dart'; import 'package:farm_tpf/presentation/screens/codes/widgets/item_code_timeline.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:farm_tpf/utils/formatter.dart';


import '../../../themes/styles_text.dart'; import '../../../themes/styles_text.dart';
import '../../custom_widgets/loading_list_page.dart';


class CodeDetailPage extends StatefulWidget { class CodeDetailPage extends StatefulWidget {
const CodeDetailPage({super.key});
final int stampId;
final String stampCode;
const CodeDetailPage({
super.key,
required this.stampId,
required this.stampCode,
});


@override @override
State<CodeDetailPage> createState() => _CodeDetailPageState(); State<CodeDetailPage> createState() => _CodeDetailPageState();
} }


class _CodeDetailPageState extends State<CodeDetailPage> { class _CodeDetailPageState extends State<CodeDetailPage> {
var bloc = DetailStampCubit();
@override
void initState() {
super.initState();
bloc.preparedData(widget.stampId);
}

@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBarWidget( appBar: AppBarWidget(
action: IconButton( action: IconButton(
onPressed: () { onPressed: () {
Get.to(() => UpdateActivityPage(
stampCode: 'AC_494D9D90',
));
// Get.to(
// () => UpdateActivityPage(
// stampCode: widget.stampCode,
// ),
// );
}, },
icon: Icon( icon: Icon(
Icons.edit, Icons.edit,
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
child: SingleChildScrollView(
child: Column(
children: [
_itemCodeDetail(
title: 'Tên sản phẩm',
detail: 'Cà rốt',
titleStyle: StylesText.body4,
detailStyle: StylesText.body4.copyWith(
color: Colors.blue,
),
child: BlocBuilder<DetailStampCubit, DetailStampState>(
bloc: bloc,
builder: (context, state) {
if (state is DetailStampLoading) {
return Center(
child: LoadingListPage(),
);
} else if (state is DetailStampFailure) {
return Center(child: Text(state.errorMessage));
} else if (state is DetailStampSuccessful) {
var stamp = state.stamp;
return SingleChildScrollView(
child: Column(
children: [
_itemCodeDetail(
title: 'Tên sản phẩm',
detail: 'Cà rốt',
titleStyle: StylesText.body4,
detailStyle: StylesText.body4.copyWith(
color: Colors.blue,
),
),
_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: 'Hạn sử dụng', detail: stamp.expiredDate?.format_DDMMYY().toString() ?? ''),
_itemCodeDetail(title: 'Mẫu tem', detail: stamp.stampType?.exampleStampName ?? ''),
const SizedBox(
height: 8,
),
Text(
'Timeline hoạt động',
style: StylesText.body1,
),
const SizedBox(
height: 8,
),
_timelineWidget(state.timeline),
],
), ),
_itemCodeDetail(title: 'Mô tả', detail: 'detail'),
_itemCodeDetail(title: 'Số lượng tem', detail: 'detail'),
_itemCodeDetail(title: 'Trạng thái', detail: 'detail'),
_itemCodeDetail(title: 'Hạn sử dụng', detail: 'detail'),
_itemCodeDetail(title: 'Mẫu tem', detail: 'detail'),
Text(
'Timeline hoạt động',
style: StylesText.body1,
),
_timelineWidget(),
],
),
),
),
);
}
return const SizedBox.shrink();
},
)),
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
// Container(
// width: 100,
// height: 100,
// color: Colors.red,
// ),
_actionButtonWidget(), _actionButtonWidget(),
], ],
), ),
); );
} }


Widget _timelineWidget() {
Widget _timelineWidget(StampTimeline timeline) {
if ((timeline.content?.length ?? 0) == 0) return const SizedBox.shrink();
return ListView.builder( return ListView.builder(
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ItemCodeTimeline( return ItemCodeTimeline(
item: timeline.content![index],
onPressed: () {}, onPressed: () {},
); );
}, },
itemCount: 20,
itemCount: timeline.content?.length ?? 0,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
); );
children: [ children: [
Expanded( Expanded(
child: SecondButton( child: SecondButton(
onPressed: () {},
onPressed: () {
bloc.updateStampStatus(
stampId: widget.stampId,
status: 'ACTIVE',
);
},
title: 'Kích hoạt toàn bộ', title: 'Kích hoạt toàn bộ',
borderColor: Colors.blue, borderColor: Colors.blue,
textColor: Colors.blue, textColor: Colors.blue,
), ),
Expanded( Expanded(
child: SecondButton( child: SecondButton(
onPressed: () {},
onPressed: () {
bloc.updateStampStatus(
stampId: widget.stampId,
status: 'CANCELED',
);
},
title: 'Huỷ toàn bộ', title: 'Huỷ toàn bộ',
borderColor: Colors.red, borderColor: Colors.red,
textColor: Colors.white, textColor: Colors.white,
children: [ children: [
Expanded( Expanded(
child: SecondButton( child: SecondButton(
onPressed: () {},
onPressed: () {
Get.to(
() => UpdateActivityPage(
stampCode: widget.stampCode,
),
)?.then((value) {
if (value != null) {
bloc.preparedData(widget.stampId);
}
});
},
title: 'Cập nhật hoạt động', title: 'Cập nhật hoạt động',
borderColor: Colors.green, borderColor: Colors.green,
textColor: Colors.green, textColor: Colors.green,

+ 6
- 1
lib/presentation/screens/codes/code_page.dart View File

: ItemCode( : ItemCode(
item: state.items?[index], item: state.items?[index],
onPressed: () { onPressed: () {
Get.to(() => CodeDetailPage());
Get.to(
() => CodeDetailPage(
stampId: state.items?[index].id,
stampCode: state.items?[index].code,
),
);
}, },
); );
}, },

+ 0
- 67
lib/presentation/screens/codes/cubit/code_update_timeline_cubit.dart View File

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';

import '../../../../data/api/app_exception.dart';
import '../../../../data/repository/repository.dart';
import '../../../../models/item_dropdown.dart';

part 'code_update_timeline_state.dart';

class CodeUpdateTimelineCubit extends Cubit<CodeUpdateTimelineState> {
CodeUpdateTimelineCubit() : super(CodeUpdateTimelineInitial());

final repository = Repository();
final formKey = GlobalKey<FormState>();
var actionDate = ValueNotifier(DateTime.now());
final descriptionCtl = TextEditingController();
var actionTypes = [
ItemDropDown(key: 'hoat dong 1', value: 'hoat dong 1'),
ItemDropDown(key: 'hoat dong 2', value: 'hoat dong 2'),
];
var selectedActionType = ValueNotifier('');
// var existedCodeUpdateTimeline = UpdateCodeUpdateTimeline();

void dispose() {
descriptionCtl.dispose();
}

Future<void> preparedData() async {
try {
await Future.delayed(const Duration(seconds: 0));
emit(CodeUpdateTimelineLoading());

emit(CodeUpdateTimelinePrepareDataSuccessful());
} catch (e) {
emit(CodeUpdateTimelineFailure(AppException.handleError(e)));
}
}

Future<void> onSubmit() async {
if (formKey.currentState!.validate()) {
// var gioiTinh = gioiTinhs.firstWhere(
// (element) => selectedGioiTinh.value == element.key,
// orElse: () => ItemDropDown(),
// );

// existedCodeUpdateTimeline
// ..gioiTinhBe = gioiTinh.value
// ..ghiChu = noteCtl.text
// ..ngayTiemUngKhiThan = ungKhiThanDate.value.convertLocalDateTimeToServer();

// UtilWidget.showLoading();
// await repository.updateCodeUpdateTimeline(
// (success) {
// UtilWidget.hideDialog();
// UtilWidget.showToastSuccess('Thêm thành công');
// Get.back();
// },
// (errorMessage) {
// UtilWidget.hideDialog();
// UtilWidget.showToastError(errorMessage);
// },
// item: existedCodeUpdateTimeline,
// );
}
}
}

+ 0
- 22
lib/presentation/screens/codes/cubit/code_update_timeline_state.dart View File

part of 'code_update_timeline_cubit.dart';

abstract class CodeUpdateTimelineState extends Equatable {
const CodeUpdateTimelineState();

@override
List<Object> get props => [];
}

class CodeUpdateTimelineInitial extends CodeUpdateTimelineState {}

class CodeUpdateTimelineLoading extends CodeUpdateTimelineState {}

class CodeUpdateTimelineFailure extends CodeUpdateTimelineState {
final String errorMessage;

CodeUpdateTimelineFailure(this.errorMessage);
}

class CodeUpdateTimelinePrepareDataSuccessful extends CodeUpdateTimelineState {
CodeUpdateTimelinePrepareDataSuccessful();
}

+ 54
- 0
lib/presentation/screens/codes/cubit/detail_stamp_cubit.dart View File

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart';

import '../../../../data/api/app_exception.dart';
import '../../../../data/repository/repository.dart';
import '../../../../utils/utils.dart';
import '../../../custom_widgets/widget_utils.dart';
import '../models/stamp.dart';

part 'detail_stamp_state.dart';

class DetailStampCubit extends Cubit<DetailStampState> {
final repository = Repository();
DetailStampCubit() : super(DetailStampInitial());

Future<void> preparedData(int stampId) async {
try {
await Future.delayed(const Duration(seconds: 0));
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)));
}
}

Future<void> updateStampStatus({
required int stampId,
required String status,
}) async {
UtilWidget.showLoading();
await repository.updateStampStatus(
(success) {
UtilWidget.hideDialog();
Utils.showSnackBarSuccess();
preparedData(stampId);
},
(errorMessage) {
UtilWidget.hideDialog();
Utils.showSnackBarError();
},
stampId: stampId,
status: status,
);
}
}

+ 24
- 0
lib/presentation/screens/codes/cubit/detail_stamp_state.dart View File

part of 'detail_stamp_cubit.dart';

abstract class DetailStampState extends Equatable {
const DetailStampState();

@override
List<Object> get props => [];
}

class DetailStampInitial extends DetailStampState {}

class DetailStampLoading extends DetailStampState {}

class DetailStampFailure extends DetailStampState {
final String errorMessage;

DetailStampFailure(this.errorMessage);
}

class DetailStampSuccessful extends DetailStampState {
final Stamp stamp;
final StampTimeline timeline;
DetailStampSuccessful(this.stamp, this.timeline);
}

+ 8
- 0
lib/presentation/screens/codes/models/stamp.dart View File

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

import '../../../../custom_model/TbCropDTO.dart'; import '../../../../custom_model/TbCropDTO.dart';


class Stamp { class Stamp {
String? status; String? status;
String? expiredDate; String? expiredDate;
String? createdDate; String? createdDate;
StampType? stampType;


Stamp({ Stamp({
this.id, this.id,
this.status, this.status,
this.expiredDate, this.expiredDate,
this.createdDate, this.createdDate,
this.stampType,
}); });


Stamp.fromJson(Map<String, dynamic> json) { Stamp.fromJson(Map<String, dynamic> json) {
status = json['status']; status = json['status'];
expiredDate = json['expiredDate']; expiredDate = json['expiredDate'];
createdDate = json['createdDate']; createdDate = json['createdDate'];
stampType = json['tbExampleStamp'] != null ? new StampType.fromJson(json['tbExampleStamp']) : null;
} }


Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
data['status'] = this.status; data['status'] = this.status;
data['expiredDate'] = this.expiredDate; data['expiredDate'] = this.expiredDate;
data['createdDate'] = this.createdDate; data['createdDate'] = this.createdDate;
if (this.stampType != null) {
data['tbExampleStamp'] = this.stampType?.toJson();
}
return data; return data;
} }
} }

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

class StampTimeline {
List<ContentTimeline>? content;

StampTimeline({this.content});

StampTimeline.fromJson(Map<String, dynamic> json) {
if (json['content'] != null) {
content = <ContentTimeline>[];
json['content'].forEach((v) {
content?.add(new ContentTimeline.fromJson(v));
});
}
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.content != null) {
data['content'] = this.content?.map((v) => v.toJson()).toList();
}
return data;
}
}

class ContentTimeline {
int? id;
int? ageDay;
int? cropId;
String? executeDate;
String? description;
String? createdDate;
int? createdById;
String? createdByName;
int? activityTypeId;
String? activityTypeName;
String? activityTypeDescription;
String? codeId;
bool? afterHarvest;

ContentTimeline(
{this.id,
this.ageDay,
this.cropId,
this.executeDate,
this.description,
this.createdDate,
this.createdById,
this.createdByName,
this.activityTypeId,
this.activityTypeName,
this.activityTypeDescription,
this.codeId,
this.afterHarvest});

ContentTimeline.fromJson(Map<String, dynamic> json) {
id = json['id'];
ageDay = json['ageDay'];
cropId = json['cropId'];
executeDate = json['executeDate'];
description = json['description'];
createdDate = json['createdDate'];
createdById = json['createdById'];
createdByName = json['createdByName'];
activityTypeId = json['activityTypeId'];
activityTypeName = json['activityTypeName'];
activityTypeDescription = json['activityTypeDescription'];
codeId = json['codeId'];
afterHarvest = json['afterHarvest'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['ageDay'] = this.ageDay;
data['cropId'] = this.cropId;
data['executeDate'] = this.executeDate;
data['description'] = this.description;
data['createdDate'] = this.createdDate;
data['createdById'] = this.createdById;
data['createdByName'] = this.createdByName;
data['activityTypeId'] = this.activityTypeId;
data['activityTypeName'] = this.activityTypeName;
data['activityTypeDescription'] = this.activityTypeDescription;
data['codeId'] = this.codeId;
data['afterHarvest'] = this.afterHarvest;
return data;
}
}

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

import 'package:farm_tpf/utils/formatter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';


import '../../../../themes/app_colors.dart'; import '../../../../themes/app_colors.dart';
import '../../../../themes/styles_text.dart'; import '../../../../themes/styles_text.dart';
import '../models/stamp_timeline.dart';


class ItemCodeTimeline extends StatelessWidget { class ItemCodeTimeline extends StatelessWidget {
final Function onPressed; final Function onPressed;
final ContentTimeline item;
const ItemCodeTimeline({ const ItemCodeTimeline({
super.key, super.key,
required this.onPressed, required this.onPressed,
required this.item,
}); });


@override @override
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
'ss - ',
'${item.activityTypeDescription ?? ''} - ',
style: StylesText.body5, style: StylesText.body5,
), ),
Text( Text(
'20/22/2202',
'${item.executeDate?.format_DDMMYY()}',
style: StylesText.caption3.copyWith(), style: StylesText.caption3.copyWith(),
) )
], ],
height: 8, height: 8,
), ),
_widgetItemInfoCompleted( _widgetItemInfoCompleted(
title: 'title',
actionDate: '2/2/2023',
nguoiThucHiens: 'nguoi thuc hien',
title: 'Mô tả',
actionDate: item.description ?? '',
location: 'dsfsdfsd f sdf dsf sd fds ',
), ),
], ],
), ),
Widget _widgetItemInfoCompleted({ Widget _widgetItemInfoCompleted({
required String title, required String title,
required String actionDate, required String actionDate,
String? nguoiThucHiens,
String? location,
}) { }) {
return Container( return Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0), padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
Expanded(
child: Text(
'Người thực hiện',
style: StylesText.caption2.copyWith(
color: AppColors.neutral1,
),
),
),
Text(
nguoiThucHiens ?? '',
style: StylesText.caption3.copyWith(
color: AppColors.neutral1,
),
),
],
child: Text(
location ?? '',
style: StylesText.caption3.copyWith(
color: AppColors.neutral1,
),
), ),
), ),
], ],

Loading…
Cancel
Save