update point

This commit is contained in:
Phuoc Nguyen
2025-12-04 11:43:27 +07:00
parent d4de557662
commit b9b6d91a87
6 changed files with 1724 additions and 430 deletions

View File

@@ -28,6 +28,7 @@ import 'package:worker/features/chat/presentation/pages/chat_list_page.dart';
import 'package:worker/features/favorites/presentation/pages/favorites_page.dart';
import 'package:worker/features/loyalty/presentation/pages/loyalty_page.dart';
import 'package:worker/features/loyalty/presentation/pages/points_history_page.dart';
import 'package:worker/features/loyalty/presentation/pages/points_record_create_page.dart';
import 'package:worker/features/loyalty/presentation/pages/points_records_page.dart';
import 'package:worker/features/loyalty/presentation/pages/rewards_page.dart';
import 'package:worker/features/main/presentation/pages/main_scaffold.dart';
@@ -323,6 +324,14 @@ final routerProvider = Provider<GoRouter>((ref) {
MaterialPage(key: state.pageKey, child: const PointsRecordsPage()),
),
// Points Record Create Route
GoRoute(
path: RouteNames.pointsRecordCreate,
name: 'loyalty_points_record_create',
pageBuilder: (context, state) =>
MaterialPage(key: state.pageKey, child: const PointsRecordCreatePage()),
),
// Orders Route
GoRoute(
path: RouteNames.orders,
@@ -651,6 +660,7 @@ class RouteNames {
static const String rewards = '$loyalty/rewards';
static const String pointsHistory = '$loyalty/points-history';
static const String pointsRecords = '$loyalty/points-records';
static const String pointsRecordCreate = '$loyalty/points-records/create';
static const String myGifts = '$loyalty/gifts';
static const String referral = '$loyalty/referral';

View File

@@ -8,11 +8,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:worker/core/widgets/loading_indicator.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/database/models/enums.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/loyalty/data/datasources/points_history_local_datasource.dart';
import 'package:worker/core/widgets/loading_indicator.dart';
import 'package:worker/features/loyalty/data/models/loyalty_point_entry_model.dart';
import 'package:worker/features/loyalty/presentation/providers/points_history_provider.dart';
@@ -20,9 +18,8 @@ import 'package:worker/features/loyalty/presentation/providers/points_history_pr
///
/// Features:
/// - Filter section with date range
/// - List of transaction cards
/// - Each card shows: description, date, amount, points change, new balance
/// - Complaint button for each transaction
/// - List of transaction cards with new design
/// - Each card shows: code, date, description, reference, points change, balance after
class PointsHistoryPage extends ConsumerWidget {
const PointsHistoryPage({super.key});
@@ -57,22 +54,19 @@ class PointsHistoryPage extends ConsumerWidget {
return _buildEmptyState(colorScheme);
}
return SingleChildScrollView(
return ListView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Filter Section
_buildFilterSection(colorScheme),
children: [
// Filter Section
_buildFilterSection(colorScheme),
const SizedBox(height: 16),
const SizedBox(height: 16),
// Transaction List
...entries.map(
(entry) => _buildTransactionCard(context, ref, entry, colorScheme),
),
],
),
// Transaction List
...entries.map(
(entry) => _buildTransactionCard(context, entry, colorScheme),
),
],
);
},
loading: () => const CustomLoadingIndicator(),
@@ -89,12 +83,12 @@ class PointsHistoryPage extends ConsumerWidget {
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Bộ lọc',
@@ -104,159 +98,162 @@ class PointsHistoryPage extends ConsumerWidget {
color: colorScheme.onSurface,
),
),
FaIcon(FontAwesomeIcons.sliders, color: colorScheme.primary, size: 18),
const SizedBox(height: 4),
Text(
'Thời gian hiệu lực: 01/01/2023 - 31/12/2023',
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
),
],
),
const SizedBox(height: 8),
Text(
'Thời gian hiệu lực: 01/01/2023 - 31/12/2023',
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
),
FaIcon(FontAwesomeIcons.filter, color: colorScheme.primary, size: 18),
],
),
),
);
}
/// Build transaction card
/// Build transaction card with new design
Widget _buildTransactionCard(
BuildContext context,
WidgetRef ref,
LoyaltyPointEntryModel entry,
ColorScheme colorScheme,
) {
final dateFormatter = DateFormat('dd/MM/yyyy HH:mm:ss');
final currencyFormatter = NumberFormat.currency(
locale: 'vi_VN',
symbol: 'VND',
decimalDigits: 0,
);
final dateFormatter = DateFormat('dd/MM/yyyy');
// Get transaction amount if it's a purchase
final datasource = ref.read(pointsHistoryLocalDataSourceProvider);
final transactionAmount = datasource.getTransactionAmount(
entry.description,
);
// Determine points color
Color pointsColor;
String pointsPrefix;
if (entry.points > 0) {
pointsColor = AppColors.success;
pointsPrefix = '+';
} else if (entry.points < 0) {
pointsColor = AppColors.danger;
pointsPrefix = '';
} else {
pointsColor = colorScheme.onSurfaceVariant;
pointsPrefix = '';
}
return Card(
elevation: 1,
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top row: Description and Complaint button
Row(
crossAxisAlignment: CrossAxisAlignment.start,
clipBehavior: Clip.antiAlias,
child: Column(
children: [
// Header: Code and Date
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
border: Border(
bottom: BorderSide(
color: colorScheme.outlineVariant,
width: 1,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
entry.entryId,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: colorScheme.primary,
),
),
Text(
dateFormatter.format(entry.timestamp),
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
// Content: Description and Points
Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Description
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Description
Text(
entry.description,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: colorScheme.primary,
fontSize: 14,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 4),
// Timestamp
Text(
'Thời gian: ${dateFormatter.format(entry.timestamp)}',
'Mã tham chiếu: #${entry.entryId}',
style: TextStyle(
fontSize: 12,
fontSize: 13,
color: colorScheme.onSurfaceVariant,
),
),
// Transaction amount (if purchase)
if (transactionAmount != null) ...[
const SizedBox(height: 2),
Text(
'Giao dịch: ${currencyFormatter.format(transactionAmount)}',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
],
],
),
),
const SizedBox(width: 12),
const SizedBox(width: 16),
// Complaint button
OutlinedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Chức năng khiếu nại đang phát triển'),
),
);
},
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
side: BorderSide(color: colorScheme.onSurfaceVariant),
foregroundColor: colorScheme.onSurface,
textStyle: const TextStyle(fontSize: 12),
minimumSize: const Size(0, 32),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
// Points
Text(
'$pointsPrefix${entry.points} điểm',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: pointsColor,
),
child: const Text('Khiếu nại'),
),
],
),
),
const SizedBox(height: 12),
// Bottom row: Points change and new balance
Row(
mainAxisAlignment: MainAxisAlignment.end,
// Footer: Balance After
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerLowest,
border: Border(
top: BorderSide(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Points change
Text(
entry.points > 0 ? '+${entry.points}' : '${entry.points}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: entry.points > 0
? AppColors.success
: entry.points < 0
? AppColors.danger
: colorScheme.onSurface,
),
),
const SizedBox(height: 2),
// New balance
Text(
'Điểm mới: ${entry.balanceAfter}',
style: TextStyle(
fontSize: 12,
color: colorScheme.primary,
),
),
],
Text(
'Số dư sau: ',
style: TextStyle(
fontSize: 13,
color: colorScheme.onSurfaceVariant,
),
),
Text(
'${entry.balanceAfter} điểm',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: colorScheme.primary,
),
),
],
),
],
),
),
],
),
);
}
@@ -293,7 +290,6 @@ class PointsHistoryPage extends ConsumerWidget {
/// Build error state
Widget _buildErrorState(Object error, ColorScheme colorScheme) {
print(error.toString());
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:worker/core/widgets/loading_indicator.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/router/app_router.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/core/widgets/loading_indicator.dart';
import 'package:worker/features/loyalty/domain/entities/points_record.dart';
import 'package:worker/features/loyalty/presentation/providers/points_records_provider.dart';
@@ -47,13 +48,12 @@ class PointsRecordsPage extends ConsumerWidget {
color: colorScheme.onSurface,
size: 20,
),
onPressed: () {
// TODO: Navigate to points record create page
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Tính năng tạo ghi nhận điểm sẽ được cập nhật'),
),
);
onPressed: () async {
final result = await context.push<bool>(RouteNames.pointsRecordCreate);
if (result == true) {
// Refresh list after successful creation
ref.invalidate(allPointsRecordsProvider);
}
},
),
const SizedBox(width: AppSpacing.sm),