Trang order
This commit is contained in:
parent
3e8bf01018
commit
c1813273b4
@ -45,7 +45,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.example.baseproject"
|
applicationId "com.example.baseproject"
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 28
|
||||||
targetSdkVersion 36
|
targetSdkVersion 36
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|||||||
@ -26,6 +26,14 @@ class CustomInterceptor extends InterceptorsWrapper {
|
|||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Xóa các field null trong query và body
|
||||||
|
options.queryParameters.removeWhere((String key, dynamic value) => value == null);
|
||||||
|
|
||||||
|
final dynamic data = options.data;
|
||||||
|
if (data is Map<String, dynamic>) {
|
||||||
|
options.data = _removeNullFields(data);
|
||||||
|
}
|
||||||
|
|
||||||
return super.onRequest(options, handler);
|
return super.onRequest(options, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +95,7 @@ class CustomInterceptor extends InterceptorsWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final dynamic errorData = err.response?.data;
|
// final dynamic errorData = err.response?.data;
|
||||||
// && err.response?.statusCode == 400
|
// && err.response?.statusCode == 400
|
||||||
// if (errorData != null && errorData["responseException"] != null) {
|
// if (errorData != null && errorData["responseException"] != null) {
|
||||||
// final dynamic temp = errorData["responseException"]["exceptionMessage"];
|
// final dynamic temp = errorData["responseException"]["exceptionMessage"];
|
||||||
@ -104,4 +112,26 @@ class CustomInterceptor extends InterceptorsWrapper {
|
|||||||
|
|
||||||
return super.onError(err, handler);
|
return super.onError(err, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _removeNullFields(Map<String, dynamic> source) {
|
||||||
|
final Map<String, dynamic> result = <String, dynamic>{};
|
||||||
|
source.forEach((String key, dynamic value) {
|
||||||
|
final dynamic cleanedValue = _cleanValue(value);
|
||||||
|
if (cleanedValue != null) {
|
||||||
|
result[key] = cleanedValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _cleanValue(dynamic value) {
|
||||||
|
if (value is Map<String, dynamic>) {
|
||||||
|
return _removeNullFields(value);
|
||||||
|
}
|
||||||
|
if (value is List) {
|
||||||
|
final List<dynamic> cleanedList = value.map(_cleanValue).where((dynamic e) => e != null).toList();
|
||||||
|
return cleanedList;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,15 @@
|
|||||||
import 'package:get_it/get_it.dart' as _i1;
|
import 'package:get_it/get_it.dart' as _i1;
|
||||||
import 'package:injectable/injectable.dart' as _i2;
|
import 'package:injectable/injectable.dart' as _i2;
|
||||||
|
|
||||||
import '../../features/presentation/account/bloc/login_bloc.dart' as _i7;
|
import '../../features/presentation/account/bloc/login_bloc.dart' as _i8;
|
||||||
import '../../features/presentation/app/bloc/user_bloc.dart' as _i3;
|
import '../../features/presentation/app/bloc/user_bloc.dart' as _i5;
|
||||||
import '../../features/repositories/hra_repository.dart' as _i6;
|
import '../../features/presentation/order/bloc/order_detail_bloc.dart' as _i9;
|
||||||
import '../../features/usecases/index.dart' as _i4;
|
import '../../features/presentation/order/bloc/order_list_bloc.dart' as _i10;
|
||||||
|
import '../../features/repositories/hra_repository.dart' as _i4;
|
||||||
|
import '../../features/usecases/index.dart' as _i6;
|
||||||
|
import '../../features/usecases/order/order_use_cases.dart' as _i3;
|
||||||
import '../../features/usecases/user/user_use_cases.dart'
|
import '../../features/usecases/user/user_use_cases.dart'
|
||||||
as _i5; // ignore_for_file: unnecessary_lambdas
|
as _i7; // ignore_for_file: unnecessary_lambdas
|
||||||
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
/// initializes the registration of provided dependencies inside of [GetIt]
|
/// initializes the registration of provided dependencies inside of [GetIt]
|
||||||
@ -27,9 +30,15 @@ _i1.GetIt $initGetIt(
|
|||||||
environment,
|
environment,
|
||||||
environmentFilter,
|
environmentFilter,
|
||||||
);
|
);
|
||||||
gh.factory<_i3.UserBloc>(() => _i3.UserBloc(get<_i4.UserUseCases>()));
|
gh.lazySingleton<_i3.OrderUseCases>(
|
||||||
gh.lazySingleton<_i5.UserUseCases>(
|
() => _i3.OrderUseCases(get<_i4.HraRepository>()));
|
||||||
() => _i5.UserUseCases(get<_i6.HraRepository>()));
|
gh.factory<_i5.UserBloc>(() => _i5.UserBloc(get<_i6.UserUseCases>()));
|
||||||
gh.factory<_i7.LoginBloc>(() => _i7.LoginBloc(get<_i5.UserUseCases>()));
|
gh.lazySingleton<_i7.UserUseCases>(
|
||||||
|
() => _i7.UserUseCases(get<_i4.HraRepository>()));
|
||||||
|
gh.factory<_i8.LoginBloc>(() => _i8.LoginBloc(get<_i7.UserUseCases>()));
|
||||||
|
gh.factory<_i9.OrderDetailBloc>(
|
||||||
|
() => _i9.OrderDetailBloc(get<_i3.OrderUseCases>()));
|
||||||
|
gh.factory<_i10.OrderListBloc>(
|
||||||
|
() => _i10.OrderListBloc(get<_i3.OrderUseCases>()));
|
||||||
return get;
|
return get;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:baseproject/core/components/alice.dart';
|
|||||||
import 'package:baseproject/core/constants/index.dart';
|
import 'package:baseproject/core/constants/index.dart';
|
||||||
import 'package:baseproject/features/presentation/app/view/app.dart';
|
import 'package:baseproject/features/presentation/app/view/app.dart';
|
||||||
import 'package:baseproject/features/repositories/hra_repository.dart';
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
|
import 'package:baseproject/features/usecases/order/order_use_cases.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
child: FormBuilder(
|
child: FormBuilder(
|
||||||
initialValue: kDebugMode
|
initialValue: kDebugMode
|
||||||
? {
|
? {
|
||||||
'userName': 'quylx',
|
'userName': 'hocsinh001',
|
||||||
'password': 'BearCMS0011002848238master',
|
'password': 'BearCMS0011002848238master',
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
|
|||||||
@ -27,6 +27,14 @@ class _HomeState extends State<Home> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text("Chào ${userInfo.fullName ?? ''}"),
|
Text("Chào ${userInfo.fullName ?? ''}"),
|
||||||
|
ConstantWidget.heightSpace16,
|
||||||
|
ConstantWidget.buildPrimaryButton(
|
||||||
|
onPressed: () {
|
||||||
|
gotoMyOrders(context);
|
||||||
|
},
|
||||||
|
text: 'Khóa học đã mua',
|
||||||
|
),
|
||||||
|
ConstantWidget.heightSpace16,
|
||||||
ConstantWidget.buildPrimaryButton(
|
ConstantWidget.buildPrimaryButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
BlocProvider.of<UserBloc>(context).logout();
|
BlocProvider.of<UserBloc>(context).logout();
|
||||||
|
|||||||
68
lib/features/presentation/order/bloc/order_detail_bloc.dart
Normal file
68
lib/features/presentation/order/bloc/order_detail_bloc.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_models.dart';
|
||||||
|
import 'package:baseproject/features/usecases/order/order_use_cases.dart';
|
||||||
|
|
||||||
|
class OrderDetailViewModel {
|
||||||
|
const OrderDetailViewModel({
|
||||||
|
this.isLoading = false,
|
||||||
|
this.order,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isLoading;
|
||||||
|
final OrderDto? order;
|
||||||
|
|
||||||
|
OrderDetailViewModel copyWith({
|
||||||
|
bool? isLoading,
|
||||||
|
OrderDto? order,
|
||||||
|
}) {
|
||||||
|
return OrderDetailViewModel(
|
||||||
|
isLoading: isLoading ?? this.isLoading,
|
||||||
|
order: order ?? this.order,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderDetailBloc extends BaseCubit<BaseStateBloc<OrderDetailViewModel>> {
|
||||||
|
OrderDetailBloc(this._orderUseCases)
|
||||||
|
: super(
|
||||||
|
InitState<OrderDetailViewModel>(
|
||||||
|
const OrderDetailViewModel(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final OrderUseCases _orderUseCases;
|
||||||
|
|
||||||
|
Future<void> loadDetail(int id) async {
|
||||||
|
final currentModel = state.model;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
LoadingState<OrderDetailViewModel>(
|
||||||
|
currentModel.copyWith(isLoading: true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await _orderUseCases.getOrderDetail(id);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(error) {
|
||||||
|
showErrorMessage(error);
|
||||||
|
emit(
|
||||||
|
LoadedState<OrderDetailViewModel>(
|
||||||
|
currentModel.copyWith(isLoading: false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(order) {
|
||||||
|
emit(
|
||||||
|
LoadedState<OrderDetailViewModel>(
|
||||||
|
currentModel.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
order: order,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
123
lib/features/presentation/order/bloc/order_list_bloc.dart
Normal file
123
lib/features/presentation/order/bloc/order_list_bloc.dart
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_models.dart';
|
||||||
|
import 'package:baseproject/features/usecases/order/order_use_cases.dart';
|
||||||
|
|
||||||
|
class OrderListViewModel {
|
||||||
|
const OrderListViewModel({
|
||||||
|
this.isLoading = false,
|
||||||
|
this.orders = const <OrderDto>[],
|
||||||
|
this.totalRows = 0,
|
||||||
|
this.pageIndex = 1,
|
||||||
|
this.pageSize = 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isLoading;
|
||||||
|
final List<OrderDto> orders;
|
||||||
|
final int totalRows;
|
||||||
|
final int pageIndex;
|
||||||
|
final int pageSize;
|
||||||
|
|
||||||
|
OrderListViewModel copyWith({
|
||||||
|
bool? isLoading,
|
||||||
|
List<OrderDto>? orders,
|
||||||
|
int? totalRows,
|
||||||
|
int? pageIndex,
|
||||||
|
int? pageSize,
|
||||||
|
}) {
|
||||||
|
return OrderListViewModel(
|
||||||
|
isLoading: isLoading ?? this.isLoading,
|
||||||
|
orders: orders ?? this.orders,
|
||||||
|
totalRows: totalRows ?? this.totalRows,
|
||||||
|
pageIndex: pageIndex ?? this.pageIndex,
|
||||||
|
pageSize: pageSize ?? this.pageSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderListBloc extends BaseCubit<BaseStateBloc<OrderListViewModel>> {
|
||||||
|
OrderListBloc(this._orderUseCases)
|
||||||
|
: super(
|
||||||
|
InitState<OrderListViewModel>(
|
||||||
|
const OrderListViewModel(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final OrderUseCases _orderUseCases;
|
||||||
|
|
||||||
|
Future<void> loadFirstPage() async {
|
||||||
|
final currentModel = state.model;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
LoadingState<OrderListViewModel>(
|
||||||
|
currentModel.copyWith(isLoading: true, pageIndex: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await _orderUseCases.getMyOrders(
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: currentModel.pageSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(error) {
|
||||||
|
emit(
|
||||||
|
LoadedState<OrderListViewModel>(
|
||||||
|
currentModel.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(data) {
|
||||||
|
emit(
|
||||||
|
LoadedState<OrderListViewModel>(
|
||||||
|
currentModel.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
pageIndex: 1,
|
||||||
|
totalRows: data.totalRows ?? 0,
|
||||||
|
orders: data.data ?? <OrderDto>[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<OrderDto>> loadMore() async {
|
||||||
|
final currentModel = state.model;
|
||||||
|
final nextPage = currentModel.pageIndex + 1;
|
||||||
|
|
||||||
|
final result = await _orderUseCases.getMyOrders(
|
||||||
|
pageIndex: nextPage,
|
||||||
|
pageSize: currentModel.pageSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.fold(
|
||||||
|
(error) => <OrderDto>[],
|
||||||
|
(data) {
|
||||||
|
final List<OrderDto> newOrders = data.data ?? <OrderDto>[];
|
||||||
|
final List<OrderDto> updatedOrders = <OrderDto>[
|
||||||
|
...currentModel.orders,
|
||||||
|
...newOrders,
|
||||||
|
];
|
||||||
|
|
||||||
|
emit(
|
||||||
|
LoadedState<OrderListViewModel>(
|
||||||
|
currentModel.copyWith(
|
||||||
|
pageIndex: nextPage,
|
||||||
|
totalRows: data.totalRows ?? currentModel.totalRows,
|
||||||
|
orders: updatedOrders,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return newOrders;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> refreshList() async {
|
||||||
|
await loadFirstPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
363
lib/features/presentation/order/view/order_detail_screen.dart
Normal file
363
lib/features/presentation/order/view/order_detail_screen.dart
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/core/components/index.dart';
|
||||||
|
import 'package:baseproject/core/language/app_localizations.dart';
|
||||||
|
import 'package:baseproject/features/presentation/order/bloc/order_detail_bloc.dart';
|
||||||
|
import 'package:baseproject/features/usecases/order/order_use_cases.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_enums.dart'
|
||||||
|
as enums;
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_models.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class OrderDetailScreen extends StatefulWidget {
|
||||||
|
const OrderDetailScreen({
|
||||||
|
Key? key,
|
||||||
|
required this.orderId,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final int orderId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OrderDetailScreen> createState() => _OrderDetailScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderDetailScreenState extends State<OrderDetailScreen> {
|
||||||
|
late final OrderDetailBloc _bloc = OrderDetailBloc(
|
||||||
|
getItSuper<OrderUseCases>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_bloc.loadDetail(widget.orderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider<OrderDetailBloc>(
|
||||||
|
create: (_) => _bloc,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Chi tiết đơn hàng'),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: BlocBuilder<OrderDetailBloc,
|
||||||
|
BaseStateBloc<OrderDetailViewModel>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final vm = state.model;
|
||||||
|
final bool isLoading =
|
||||||
|
state is LoadingState<OrderDetailViewModel> || vm.isLoading;
|
||||||
|
|
||||||
|
if (isLoading && vm.order == null) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.order == null) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('Không tìm thấy đơn hàng'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buildDetailContent(context, vm.order!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDetailContent(BuildContext context, OrderDto order) {
|
||||||
|
final appLoc = AppLocalizations.of(context)!;
|
||||||
|
final DateTime? date = order.paidDate ?? order.createdDate;
|
||||||
|
final String dateText =
|
||||||
|
appLoc.displayDateTime(date, isFullTime: true, isDateOfMonth: true);
|
||||||
|
|
||||||
|
final String studentName = order.fullName ?? '';
|
||||||
|
final String phone = order.phone ?? '';
|
||||||
|
final String address = order.address ?? '';
|
||||||
|
|
||||||
|
final _OrderStatusUI statusUI = _getStatusUI(order.status);
|
||||||
|
|
||||||
|
final String amountText = order.totalAmount != null
|
||||||
|
? appLoc.displayNumber(order.totalAmount)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
final List<OrderItemDto> items = order.items ?? <OrderItemDto>[];
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: <Widget>[
|
||||||
|
_buildInfoRow(
|
||||||
|
context,
|
||||||
|
leftLabel: 'Ngày đặt',
|
||||||
|
leftValue: dateText,
|
||||||
|
rightLabel: 'Người nhận',
|
||||||
|
rightValue: studentName,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildInfoRow(
|
||||||
|
context,
|
||||||
|
leftLabel: 'Số điện thoại',
|
||||||
|
leftValue: phone,
|
||||||
|
rightLabel: 'Địa chỉ giao hàng',
|
||||||
|
rightValue: address,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildStatusRow(context, statusUI),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
...items.map((e) => _buildItem(context, e)).toList(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: <Color>[
|
||||||
|
Color(0xFFFFF8E1),
|
||||||
|
Color(0xFFFFECB3),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text(
|
||||||
|
'Tổng tiền',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
if (amountText.isNotEmpty)
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: amountText,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
children: const <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: ' đ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoRow(
|
||||||
|
BuildContext context, {
|
||||||
|
required String leftLabel,
|
||||||
|
required String leftValue,
|
||||||
|
required String rightLabel,
|
||||||
|
required String rightValue,
|
||||||
|
}) {
|
||||||
|
return Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: _InfoBox(
|
||||||
|
label: leftLabel,
|
||||||
|
value: leftValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: _InfoBox(
|
||||||
|
label: rightLabel,
|
||||||
|
value: rightValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusRow(BuildContext context, _OrderStatusUI statusUI) {
|
||||||
|
return Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: _InfoBox(
|
||||||
|
label: 'Trạng thái',
|
||||||
|
valueWidget: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: Border.all(color: statusUI.backgroundColor),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
statusUI.label,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: statusUI.backgroundColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Expanded(child: SizedBox()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(BuildContext context, OrderItemDto item) {
|
||||||
|
final appLoc = AppLocalizations.of(context)!;
|
||||||
|
final String name = item.product?.name ?? 'Khóa học';
|
||||||
|
final String image = item.product?.image ?? '';
|
||||||
|
final int quantity = item.quantity ?? 1;
|
||||||
|
final double? price = item.totalPrice ?? item.salePrice ?? item.unitPrice;
|
||||||
|
final String priceText =
|
||||||
|
price != null ? appLoc.displayNumber(price) : '';
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
CustomImage(
|
||||||
|
imageUrl: image,
|
||||||
|
width: 56,
|
||||||
|
height: 56,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
if (priceText.isNotEmpty)
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: '$quantity x ',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: priceText,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const TextSpan(
|
||||||
|
text: ' đ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_OrderStatusUI _getStatusUI(enums.OrderStatusEnum? status) {
|
||||||
|
if (status == enums.OrderStatusEnum.value_4 ||
|
||||||
|
status == enums.OrderStatusEnum.value_5) {
|
||||||
|
return const _OrderStatusUI(
|
||||||
|
label: 'Thành công',
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const _OrderStatusUI(
|
||||||
|
label: 'Chờ liên hệ',
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfoBox extends StatelessWidget {
|
||||||
|
const _InfoBox({
|
||||||
|
Key? key,
|
||||||
|
required this.label,
|
||||||
|
this.value,
|
||||||
|
this.valueWidget,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final String? value;
|
||||||
|
final Widget? valueWidget;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: const Color(0xFF1976D2)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
valueWidget ??
|
||||||
|
Text(
|
||||||
|
value ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderStatusUI {
|
||||||
|
const _OrderStatusUI({
|
||||||
|
required this.label,
|
||||||
|
required this.backgroundColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final Color backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
219
lib/features/presentation/order/view/order_list_screen.dart
Normal file
219
lib/features/presentation/order/view/order_list_screen.dart
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/core/components/index.dart';
|
||||||
|
import 'package:baseproject/core/language/app_localizations.dart';
|
||||||
|
import 'package:baseproject/features/presentation/order/bloc/order_list_bloc.dart';
|
||||||
|
import 'package:baseproject/features/presentation/order/view/order_detail_screen.dart';
|
||||||
|
import 'package:baseproject/features/usecases/order/order_use_cases.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_models.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_enums.dart' as enums;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class OrderListScreen extends StatefulWidget {
|
||||||
|
const OrderListScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OrderListScreen> createState() => _OrderListScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderListScreenState extends State<OrderListScreen> {
|
||||||
|
late final OrderListBloc _bloc = OrderListBloc(
|
||||||
|
getItSuper<OrderUseCases>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_bloc.loadFirstPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider<OrderListBloc>(
|
||||||
|
create: (_) => _bloc,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Khóa học đã mua'),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: BlocBuilder<OrderListBloc, BaseStateBloc<OrderListViewModel>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final vm = state.model;
|
||||||
|
final bool isLoading = state is LoadingState<OrderListViewModel> || vm.isLoading;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
if (isLoading && vm.orders.isEmpty) const LinearProgressIndicator(),
|
||||||
|
Expanded(
|
||||||
|
child: CustomListView<OrderDto>(
|
||||||
|
totalItem: vm.totalRows,
|
||||||
|
items: vm.orders,
|
||||||
|
onRefresh: _bloc.refreshList,
|
||||||
|
onLoading: () async {
|
||||||
|
final List<OrderDto> newItems = await _bloc.loadMore();
|
||||||
|
return newItems;
|
||||||
|
},
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (index >= vm.orders.length) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
final OrderDto order = vm.orders[index];
|
||||||
|
return _buildOrderItem(context, order);
|
||||||
|
},
|
||||||
|
separatorWidget: const Divider(height: 1),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOrderItem(BuildContext context, OrderDto order) {
|
||||||
|
final appLoc = AppLocalizations.of(context)!;
|
||||||
|
final DateTime? date = order.paidDate ?? order.createdDate;
|
||||||
|
final String dateText = appLoc.displayDateTime(date, isFullTime: true, isDateOfMonth: true);
|
||||||
|
|
||||||
|
final String studentName = order.fullName ?? '';
|
||||||
|
final String phone = order.phone ?? '';
|
||||||
|
|
||||||
|
final _OrderStatusUI statusUI = _getStatusUI(order.status);
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFE3F2FD),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
dateText,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color(0xFF1976D2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: statusUI.backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
statusUI.label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'$studentName • $phone',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text(
|
||||||
|
'Tổng tiền:',
|
||||||
|
style: TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
if (order.totalAmount != null)
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.displayCurrency(order.totalAmount ?? 0),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (order.id != null) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder: (_) => OrderDetailScreen(
|
||||||
|
orderId: order.id!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Chi tiết →',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_OrderStatusUI _getStatusUI(enums.OrderStatusEnum? status) {
|
||||||
|
if (status == enums.OrderStatusEnum.value_4 || status == enums.OrderStatusEnum.value_5) {
|
||||||
|
return const _OrderStatusUI(
|
||||||
|
label: 'Thành công',
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const _OrderStatusUI(
|
||||||
|
label: 'Chờ liên hệ',
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderStatusUI {
|
||||||
|
const _OrderStatusUI({
|
||||||
|
required this.label,
|
||||||
|
required this.backgroundColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final Color backgroundColor;
|
||||||
|
}
|
||||||
202
lib/features/presentation/zoom/view/zoom_join_screen.dart
Normal file
202
lib/features/presentation/zoom/view/zoom_join_screen.dart
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ZoomJoinScreen extends StatelessWidget {
|
||||||
|
const ZoomJoinScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Placeholder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// import 'package:baseproject/core/components/constants_widget.dart';
|
||||||
|
// import 'package:baseproject/core/theme/size.dart';
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart';
|
||||||
|
|
||||||
|
// class ZoomJoinScreen extends StatefulWidget {
|
||||||
|
// const ZoomJoinScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// State<ZoomJoinScreen> createState() => _ZoomJoinScreenState();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class _ZoomJoinScreenState extends State<ZoomJoinScreen> {
|
||||||
|
// final TextEditingController _sessionNameController = TextEditingController();
|
||||||
|
// final TextEditingController _userNameController = TextEditingController(text: 'Guest');
|
||||||
|
// final TextEditingController _tokenController = TextEditingController();
|
||||||
|
// final TextEditingController _passwordController = TextEditingController();
|
||||||
|
|
||||||
|
// final ZoomVideoSdk _zoom = ZoomVideoSdk();
|
||||||
|
// bool _isInitializing = false;
|
||||||
|
// bool _isInitialized = false;
|
||||||
|
// bool _isJoining = false;
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// void initState() {
|
||||||
|
// super.initState();
|
||||||
|
// _initZoomSdk();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// void dispose() {
|
||||||
|
// _sessionNameController.dispose();
|
||||||
|
// _userNameController.dispose();
|
||||||
|
// _tokenController.dispose();
|
||||||
|
// _passwordController.dispose();
|
||||||
|
// super.dispose();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<void> _initZoomSdk() async {
|
||||||
|
// if (_isInitializing || _isInitialized) return;
|
||||||
|
// setState(() {
|
||||||
|
// _isInitializing = true;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// final initConfig = InitConfig(
|
||||||
|
// domain: 'zoom.us',
|
||||||
|
// enableLog: true,
|
||||||
|
// );
|
||||||
|
// await _zoom.initSdk(initConfig);
|
||||||
|
// setState(() {
|
||||||
|
// _isInitialized = true;
|
||||||
|
// });
|
||||||
|
// } catch (e) {
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
// SnackBar(
|
||||||
|
// content: Text('Khởi tạo Zoom SDK thất bại: $e'),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// } finally {
|
||||||
|
// if (mounted) {
|
||||||
|
// setState(() {
|
||||||
|
// _isInitializing = false;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<void> _onJoin() async {
|
||||||
|
// if (!_isInitialized) {
|
||||||
|
// await _initZoomSdk();
|
||||||
|
// if (!_isInitialized) return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// final String sessionName = _sessionNameController.text.trim();
|
||||||
|
// final String userName = _userNameController.text.trim();
|
||||||
|
// final String token = _tokenController.text.trim();
|
||||||
|
// final String password = _passwordController.text.trim();
|
||||||
|
|
||||||
|
// if (sessionName.isEmpty || token.isEmpty) {
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
// const SnackBar(
|
||||||
|
// content: Text('Vui lòng nhập đủ Session name và Token'),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// setState(() {
|
||||||
|
// _isJoining = true;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// final Map<String, bool> audioOptions = {
|
||||||
|
// 'connect': true,
|
||||||
|
// 'mute': false,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// final Map<String, bool> videoOptions = {
|
||||||
|
// 'localVideoOn': true,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// final JoinSessionConfig joinSession = JoinSessionConfig(
|
||||||
|
// sessionName: sessionName,
|
||||||
|
// sessionPassword: password.isEmpty ? null : password,
|
||||||
|
// token: token,
|
||||||
|
// userName: userName.isEmpty ? 'Guest' : userName,
|
||||||
|
// audioOptions: audioOptions,
|
||||||
|
// videoOptions: videoOptions,
|
||||||
|
// sessionIdleTimeoutMins: 40,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// await _zoom.joinSession(joinSession);
|
||||||
|
// } catch (e) {
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
// SnackBar(
|
||||||
|
// content: Text('Không thể join Zoom: $e'),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// } finally {
|
||||||
|
// if (mounted) {
|
||||||
|
// setState(() {
|
||||||
|
// _isJoining = false;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return Scaffold(
|
||||||
|
// appBar: AppBar(
|
||||||
|
// title: const Text('Tham gia Zoom (Video SDK)'),
|
||||||
|
// ),
|
||||||
|
// body: SafeArea(
|
||||||
|
// child: Padding(
|
||||||
|
// padding: const EdgeInsets.all(kPaddingDefault),
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
// children: <Widget>[
|
||||||
|
// ConstantWidget.textBodyDefault(
|
||||||
|
// 'Nhập thông tin phiên Zoom Video SDK (session) được backend cấp: Session name, Token, mật khẩu (nếu có).',
|
||||||
|
// textAlign: TextAlign.left,
|
||||||
|
// ),
|
||||||
|
// ConstantWidget.heightSpace16,
|
||||||
|
// TextField(
|
||||||
|
// controller: _sessionNameController,
|
||||||
|
// decoration: const InputDecoration(
|
||||||
|
// labelText: 'Session name',
|
||||||
|
// border: OutlineInputBorder(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ConstantWidget.heightSpace16,
|
||||||
|
// TextField(
|
||||||
|
// controller: _userNameController,
|
||||||
|
// decoration: const InputDecoration(
|
||||||
|
// labelText: 'Tên hiển thị',
|
||||||
|
// border: OutlineInputBorder(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ConstantWidget.heightSpace16,
|
||||||
|
// TextField(
|
||||||
|
// controller: _tokenController,
|
||||||
|
// decoration: const InputDecoration(
|
||||||
|
// labelText: 'SDK JWT Token',
|
||||||
|
// hintText: 'Token từ server Zoom/Backend',
|
||||||
|
// border: OutlineInputBorder(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ConstantWidget.heightSpace16,
|
||||||
|
// TextField(
|
||||||
|
// controller: _passwordController,
|
||||||
|
// decoration: const InputDecoration(
|
||||||
|
// labelText: 'Mật khẩu (nếu có)',
|
||||||
|
// border: OutlineInputBorder(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ConstantWidget.heightSpace24,
|
||||||
|
// SizedBox(
|
||||||
|
// height: 48,
|
||||||
|
// child: ConstantWidget.buildPrimaryButton(
|
||||||
|
// onPressed: (_isInitializing || _isJoining) ? null : _onJoin,
|
||||||
|
// text: _isJoining ? 'Đang join...' : 'Tham gia',
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -1,3 +1,4 @@
|
|||||||
const String appInitRouteName = '/app_init';
|
const String appInitRouteName = '/app_init';
|
||||||
const String loginRouteName = '/login';
|
const String loginRouteName = '/login';
|
||||||
const String homeApp = '/home_app';
|
const String homeApp = '/home_app';
|
||||||
|
const String myOrderRouteName = '/my_orders';
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:baseproject/features/presentation/account/login_screen.dart';
|
import 'package:baseproject/features/presentation/account/login_screen.dart';
|
||||||
import 'package:baseproject/features/presentation/app/view/init_screen.dart';
|
import 'package:baseproject/features/presentation/app/view/init_screen.dart';
|
||||||
import 'package:baseproject/features/presentation/home/view/home.dart';
|
import 'package:baseproject/features/presentation/home/view/home.dart';
|
||||||
|
import 'package:baseproject/features/presentation/order/view/order_list_screen.dart';
|
||||||
import 'package:baseproject/features/route/route_const.dart';
|
import 'package:baseproject/features/route/route_const.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ class RouteGenerator {
|
|||||||
return MaterialPageRoute<void>(settings: setting, builder: (_) => const Home());
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const Home());
|
||||||
case loginRouteName:
|
case loginRouteName:
|
||||||
return MaterialPageRoute<void>(settings: setting, builder: (_) => const LoginScreen());
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const LoginScreen());
|
||||||
|
case myOrderRouteName:
|
||||||
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const OrderListScreen());
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,3 +12,7 @@ void gotoHome(BuildContext context) {
|
|||||||
Navigator.pushReplacementNamed(context, homeApp);
|
Navigator.pushReplacementNamed(context, homeApp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gotoMyOrders(BuildContext context) {
|
||||||
|
Navigator.pushNamed(context, myOrderRouteName);
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export 'user/user_use_cases.dart';
|
export 'user/user_use_cases.dart';
|
||||||
|
export 'order/order_use_cases.dart';
|
||||||
|
|||||||
54
lib/features/usecases/order/order_use_cases.dart
Normal file
54
lib/features/usecases/order/order_use_cases.dart
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository_models.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
class OrderUseCases {
|
||||||
|
OrderUseCases(this._hraRepository);
|
||||||
|
|
||||||
|
final HraRepository _hraRepository;
|
||||||
|
|
||||||
|
Future<Either<String, OrderDtoFilterResult>> getMyOrders({
|
||||||
|
required int pageIndex,
|
||||||
|
required int pageSize,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final query = OrderGetListQuery(
|
||||||
|
pageIndex: pageIndex,
|
||||||
|
pageSize: pageSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await _hraRepository.orderListMy(query);
|
||||||
|
|
||||||
|
if (result.data == null || result.success != true) {
|
||||||
|
return Left<String, OrderDtoFilterResult>(
|
||||||
|
result.message ?? 'Không lấy được danh sách khóa học đã mua',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Right<String, OrderDtoFilterResult>(result.data!);
|
||||||
|
} catch (ex) {
|
||||||
|
showErrorMessage(ex.toString());
|
||||||
|
return Left<String, OrderDtoFilterResult>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<String, OrderDto>> getOrderDetail(int id) async {
|
||||||
|
try {
|
||||||
|
final OrderDtoApiResponse result = await _hraRepository.orderId(id);
|
||||||
|
|
||||||
|
if (result.data == null || result.success != true) {
|
||||||
|
return Left<String, OrderDto>(
|
||||||
|
result.message ?? 'Không lấy được thông tin đơn hàng',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Right<String, OrderDto>(result.data!);
|
||||||
|
} catch (ex) {
|
||||||
|
return Left<String, OrderDto>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ dependencies:
|
|||||||
flutter_staggered_grid_view: ^0.7.0
|
flutter_staggered_grid_view: ^0.7.0
|
||||||
pull_to_refresh: ^2.0.0
|
pull_to_refresh: ^2.0.0
|
||||||
dartz: ^0.10.1
|
dartz: ^0.10.1
|
||||||
|
# flutter_zoom_videosdk: ^2.3.0
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user