2025-08-29 20:34:26 +07:00

479 lines
13 KiB
Dart

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import '../../../common/theme/theme.dart';
// Models
class Reward {
final String id;
final String name;
final String image;
final int pointsUsed;
final String description;
final DateTime redeemedAt;
final RewardStatus status;
final String? couponCode;
final String? validUntil;
final String? termsAndConditions;
final String categoryName;
final String categoryIcon;
Reward({
required this.id,
required this.name,
required this.image,
required this.pointsUsed,
required this.description,
required this.redeemedAt,
required this.status,
required this.categoryName,
required this.categoryIcon,
this.couponCode,
this.validUntil,
this.termsAndConditions,
});
String get statusText {
switch (status) {
case RewardStatus.active:
return "Aktif";
case RewardStatus.used:
return "Sudah Digunakan";
case RewardStatus.expired:
return "Kadaluarsa";
}
}
Color get statusColor {
switch (status) {
case RewardStatus.active:
return AppColor.success;
case RewardStatus.used:
return AppColor.textSecondary;
case RewardStatus.expired:
return AppColor.error;
}
}
}
enum RewardStatus { active, used, expired }
@RoutePage()
class RewardPage extends StatefulWidget {
const RewardPage({super.key});
@override
State<RewardPage> createState() => _RewardPageState();
}
class _RewardPageState extends State<RewardPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// Sample reward data
final List<Reward> rewards = [
Reward(
id: "r1",
name: "Es Teh Manis",
image: "🧊",
pointsUsed: 1500,
description: "Teh manis dingin segar",
redeemedAt: DateTime.now().subtract(Duration(hours: 2)),
status: RewardStatus.active,
categoryName: "Minuman",
categoryIcon: "🥤",
couponCode: "ETM123456",
validUntil: "31 Des 2025",
termsAndConditions:
"Berlaku untuk dine-in dan take away. Tidak dapat digabung dengan promo lain.",
),
Reward(
id: "r2",
name: "Diskon 50%",
image: "🏷️",
pointsUsed: 5000,
description: "Potongan harga 50% untuk semua menu",
redeemedAt: DateTime.now().subtract(Duration(days: 1)),
status: RewardStatus.used,
categoryName: "Voucher",
categoryIcon: "🎟️",
couponCode: "DISC50789",
validUntil: "15 Des 2025",
termsAndConditions:
"Berlaku untuk pembelian minimum Rp 50.000. Tidak berlaku untuk menu promo.",
),
Reward(
id: "r3",
name: "Nasi Gudeg",
image: "🍛",
pointsUsed: 4000,
description: "Gudeg Jogja autentik",
redeemedAt: DateTime.now().subtract(Duration(days: 3)),
status: RewardStatus.active,
categoryName: "Makanan",
categoryIcon: "🍽️",
couponCode: "GUDEG456",
validUntil: "25 Des 2025",
termsAndConditions:
"Berlaku untuk 1 porsi. Dapat dimakan di tempat atau dibawa pulang.",
),
Reward(
id: "r4",
name: "Gratis Ongkir",
image: "🚚",
pointsUsed: 2000,
description: "Bebas ongkos kirim untuk pesanan apapun",
redeemedAt: DateTime.now().subtract(Duration(days: 15)),
status: RewardStatus.expired,
categoryName: "Voucher",
categoryIcon: "🎟️",
validUntil: "20 Agu 2025",
termsAndConditions:
"Berlaku untuk area Jabodetabek. Minimum pembelian Rp 25.000.",
),
Reward(
id: "r5",
name: "Kopi Susu",
image: "",
pointsUsed: 2000,
description: "Kopi dengan susu creamy",
redeemedAt: DateTime.now().subtract(Duration(days: 7)),
status: RewardStatus.used,
categoryName: "Minuman",
categoryIcon: "🥤",
couponCode: "KOPI987654",
validUntil: "30 Nov 2025",
termsAndConditions:
"Berlaku untuk size regular. Dapat request level manis.",
),
];
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
List<Reward> get filteredRewards {
switch (_tabController.index) {
case 0:
return rewards; // Semua
case 1:
return rewards.where((r) => r.status == RewardStatus.active).toList();
case 2:
return rewards.where((r) => r.status == RewardStatus.used).toList();
case 3:
return rewards.where((r) => r.status == RewardStatus.expired).toList();
default:
return rewards;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.backgroundLight,
appBar: AppBar(title: Text("Reward Saya"), centerTitle: true),
body: Column(
children: [
_buildCleanTabBar(),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildRewardList(), // Semua
_buildRewardList(), // Aktif
_buildRewardList(), // Digunakan
_buildRewardList(), // Kadaluarsa
],
),
),
],
),
);
}
Widget _buildCleanTabBar() {
final activeCount = rewards
.where((r) => r.status == RewardStatus.active)
.length;
final usedCount = rewards
.where((r) => r.status == RewardStatus.used)
.length;
final expiredCount = rewards
.where((r) => r.status == RewardStatus.expired)
.length;
return Container(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 20,
offset: Offset(0, 4),
),
],
),
child: TabBar(
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
isScrollable: true,
tabAlignment: TabAlignment.start,
dividerColor: Colors.transparent,
onTap: (index) => setState(() {}),
tabs: [
_buildCleanTab("Semua", rewards.length),
_buildCleanTab("Aktif", activeCount),
_buildCleanTab("Terpakai", usedCount),
_buildCleanTab("Expired", expiredCount),
],
),
);
}
Widget _buildCleanTab(String label, int count) {
return Container(height: 44, child: Center(child: Text("$label ($count)")));
}
Widget _buildRewardList() {
final filteredData = filteredRewards;
if (filteredData.isEmpty) {
return _buildEmptyState();
}
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 8),
itemCount: filteredData.length,
itemBuilder: (context, index) {
final reward = filteredData[index];
return _buildCleanRewardCard(reward);
},
);
}
Widget _buildEmptyState() {
String message;
String icon;
switch (_tabController.index) {
case 1:
message = "Belum ada reward aktif";
icon = "🎯";
break;
case 2:
message = "Belum ada reward yang digunakan";
icon = "";
break;
case 3:
message = "Tidak ada reward yang kadaluarsa";
icon = "";
break;
default:
message = "Belum ada reward";
icon = "🎁";
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(icon, style: TextStyle(fontSize: 64)),
SizedBox(height: 16),
Text(
message,
style: AppStyle.lg.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 8),
Text(
"Tukar poin Anda untuk mendapatkan reward menarik",
style: AppStyle.sm.copyWith(color: AppColor.textLight),
textAlign: TextAlign.center,
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () => context.router.back(),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primary,
foregroundColor: AppColor.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
elevation: 0,
),
child: Text(
"Tukar Poin Sekarang",
style: AppStyle.sm.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.white,
),
),
),
],
),
);
}
Widget _buildCleanRewardCard(Reward reward) {
return Container(
margin: EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 20,
offset: Offset(0, 4),
),
],
),
child: InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: [
// Product Image - Simplified
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: AppColor.backgroundLight,
borderRadius: BorderRadius.circular(14),
),
child: Center(
child: Text(reward.image, style: TextStyle(fontSize: 24)),
),
),
SizedBox(width: 16),
// Reward Info - Cleaner layout
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
reward.name,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
),
_buildStatusBadge(reward),
],
),
SizedBox(height: 4),
Text(
reward.description,
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
SizedBox(height: 8),
Row(
children: [
Icon(
Icons.access_time,
size: 12,
color: AppColor.textLight,
),
SizedBox(width: 4),
Text(
_formatDate(reward.redeemedAt),
style: AppStyle.xs.copyWith(
color: AppColor.textLight,
),
),
Spacer(),
Icon(Icons.stars, size: 14, color: AppColor.warning),
SizedBox(width: 4),
Text(
"${reward.pointsUsed}",
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
),
],
),
),
),
);
}
Widget _buildStatusBadge(Reward reward) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: reward.statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Text(
reward.statusText,
style: AppStyle.xs.copyWith(
color: reward.statusColor,
fontWeight: FontWeight.w600,
),
),
);
}
String _formatDate(DateTime date) {
final months = [
'',
'Jan',
'Feb',
'Mar',
'Apr',
'Mei',
'Jun',
'Jul',
'Agu',
'Sep',
'Okt',
'Nov',
'Des',
];
final now = DateTime.now();
final difference = now.difference(date);
if (difference.inDays == 0) {
if (difference.inHours == 0) {
return "${difference.inMinutes} menit lalu";
}
return "${difference.inHours} jam lalu";
} else if (difference.inDays == 1) {
return "Kemarin";
} else if (difference.inDays < 7) {
return "${difference.inDays} hari lalu";
} else {
return "${date.day} ${months[date.month]} ${date.year}";
}
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
}