- Wallet EnakPoint & EnakCoin: beranda, riwayat, saldo kedaluwarsa, kode bayar, tukar EnakCoin, transfer - PIN (buat, ganti, lupa), outlet, riwayat order, game spin - Halaman community, webview, coming soon, dan ikon menu beranda baru - Redesign Reward Saya: kartu tiket, filter chip, detail kode + QR - Hapus customer point loader, menu, dan checkout lama - Base URL ke enaklo-pos-api.altru.id Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
2.9 KiB
Dart
99 lines
2.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
import '../../../common/theme/theme.dart';
|
|
import '../../../sample/reward_sample_data.dart';
|
|
|
|
/// Reward aktif dengan sisa masa berlaku segini dianggap segera berakhir.
|
|
const int rewardExpiringSoonDays = 7;
|
|
|
|
extension RewardStatusUi on RewardStatus {
|
|
String get label => switch (this) {
|
|
RewardStatus.active => 'Aktif',
|
|
RewardStatus.used => 'Terpakai',
|
|
RewardStatus.expired => 'Kedaluwarsa',
|
|
};
|
|
|
|
Color get color => switch (this) {
|
|
RewardStatus.active => AppColor.success,
|
|
RewardStatus.used => AppColor.textSecondary,
|
|
RewardStatus.expired => AppColor.error,
|
|
};
|
|
}
|
|
|
|
/// Tanggal pendek: "1 Okt 2026".
|
|
String formatRewardDate(DateTime date) =>
|
|
DateFormat('d MMM yyyy', 'id_ID').format(date);
|
|
|
|
/// Sisa hari kalender sampai [Reward.validUntil]; 0 berarti berakhir hari ini.
|
|
int rewardDaysLeft(Reward reward) {
|
|
final now = DateTime.now();
|
|
final today = DateTime(now.year, now.month, now.day);
|
|
final end = DateTime(
|
|
reward.validUntil.year,
|
|
reward.validUntil.month,
|
|
reward.validUntil.day,
|
|
);
|
|
return end.difference(today).inDays;
|
|
}
|
|
|
|
bool isRewardExpiringSoon(Reward reward) =>
|
|
reward.status == RewardStatus.active &&
|
|
rewardDaysLeft(reward) <= rewardExpiringSoonDays;
|
|
|
|
/// "Berakhir hari ini" / "Berakhir 3 hari lagi" / "Berlaku s/d 25 Okt 2026".
|
|
String rewardValidityLabel(Reward reward) {
|
|
final daysLeft = rewardDaysLeft(reward);
|
|
if (daysLeft <= 0) return 'Berakhir hari ini';
|
|
if (daysLeft <= rewardExpiringSoonDays) {
|
|
return 'Berakhir $daysLeft hari lagi';
|
|
}
|
|
return 'Berlaku s/d ${formatRewardDate(reward.validUntil)}';
|
|
}
|
|
|
|
class RewardStatusBadge extends StatelessWidget {
|
|
const RewardStatusBadge({super.key, required this.status});
|
|
|
|
final RewardStatus status;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
|
decoration: BoxDecoration(
|
|
color: status.color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
status.label,
|
|
style: AppStyle.xs.copyWith(
|
|
color: status.color,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Emoji reward; abu-abu untuk reward yang sudah tidak bisa dipakai.
|
|
class RewardEmoji extends StatelessWidget {
|
|
const RewardEmoji({super.key, required this.reward, required this.size});
|
|
|
|
final Reward reward;
|
|
final double size;
|
|
|
|
static const ColorFilter _greyscale = ColorFilter.matrix([
|
|
0.2126, 0.7152, 0.0722, 0, 0, //
|
|
0.2126, 0.7152, 0.0722, 0, 0, //
|
|
0.2126, 0.7152, 0.0722, 0, 0, //
|
|
0, 0, 0, 0.5, 0, //
|
|
]);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final emoji = Text(reward.emoji, style: TextStyle(fontSize: size));
|
|
if (reward.status == RewardStatus.active) return emoji;
|
|
return ColorFiltered(colorFilter: _greyscale, child: emoji);
|
|
}
|
|
}
|