141 lines
4.2 KiB
Dart
141 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
||||
|
|
import 'package:intl/intl.dart';
|
|||
|
|
|
|||
|
|
import '../../../common/theme/theme.dart';
|
|||
|
|
import '../../../domain/wallet/wallet.dart';
|
|||
|
|
import '../../../sample/promo_sample_data.dart';
|
|||
|
|
import '../../components/assets/assets.gen.dart';
|
|||
|
|
import '../wallet/wallet_ui.dart';
|
|||
|
|
|
|||
|
|
/// Promo aktif dengan sisa hari segini dianggap segera berakhir.
|
|||
|
|
const int promoEndingSoonDays = 3;
|
|||
|
|
|
|||
|
|
extension PromoTypeUi on PromoType {
|
|||
|
|
String get label => switch (this) {
|
|||
|
|
PromoType.point => 'EnakPoint',
|
|||
|
|
PromoType.coin => 'EnakCoin',
|
|||
|
|
PromoType.reward => 'Hadiah',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// Warna aksen untuk teks dan latar tipis; cukup gelap di atas putih.
|
|||
|
|
Color get color => switch (this) {
|
|||
|
|
PromoType.point => AppColor.primary,
|
|||
|
|
PromoType.coin => const Color(0xFFC26A00),
|
|||
|
|
PromoType.reward => AppColor.secondaryDark,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// Latar banner; teks putih di atasnya.
|
|||
|
|
List<Color> get gradient => switch (this) {
|
|||
|
|
PromoType.point => AppColor.primaryGradient,
|
|||
|
|
PromoType.coin => const [Color(0xFFF59E0B), Color(0xFFC2410C)],
|
|||
|
|
PromoType.reward => const [Color(0xFF43A047), Color(0xFF1B5E20)],
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool isPromoUpcoming(Promo promo) => promo.startDate.isAfter(DateTime.now());
|
|||
|
|
|
|||
|
|
/// Sisa hari kalender sampai [Promo.endDate]; 0 berarti berakhir hari ini.
|
|||
|
|
int promoDaysLeft(Promo promo) {
|
|||
|
|
final now = DateTime.now();
|
|||
|
|
final today = DateTime(now.year, now.month, now.day);
|
|||
|
|
final end = DateTime(
|
|||
|
|
promo.endDate.year,
|
|||
|
|
promo.endDate.month,
|
|||
|
|
promo.endDate.day,
|
|||
|
|
);
|
|||
|
|
return end.difference(today).inDays;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool isPromoEndingSoon(Promo promo) =>
|
|||
|
|
!isPromoUpcoming(promo) && promoDaysLeft(promo) <= promoEndingSoonDays;
|
|||
|
|
|
|||
|
|
/// "12 Okt".
|
|||
|
|
String formatPromoDate(DateTime date) =>
|
|||
|
|
DateFormat('d MMM', 'id_ID').format(date);
|
|||
|
|
|
|||
|
|
/// "1 Okt – 25 Okt 2026".
|
|||
|
|
String promoPeriodLabel(Promo promo) =>
|
|||
|
|
'${formatPromoDate(promo.startDate)} – '
|
|||
|
|
'${DateFormat('d MMM yyyy', 'id_ID').format(promo.endDate)}';
|
|||
|
|
|
|||
|
|
String promoOutletLabel(Promo promo) =>
|
|||
|
|
promo.outlets.isEmpty ? 'Semua outlet' : promo.outlets.join(', ');
|
|||
|
|
|
|||
|
|
/// "Mulai 4 Okt" / "Berakhir 2 hari lagi" / "s/d 25 Okt".
|
|||
|
|
String promoTimeLabel(Promo promo) {
|
|||
|
|
if (isPromoUpcoming(promo)) {
|
|||
|
|
return 'Mulai ${formatPromoDate(promo.startDate)}';
|
|||
|
|
}
|
|||
|
|
final daysLeft = promoDaysLeft(promo);
|
|||
|
|
if (daysLeft <= 0) return 'Berakhir hari ini';
|
|||
|
|
if (daysLeft <= promoEndingSoonDays) return 'Berakhir $daysLeft hari lagi';
|
|||
|
|
return 's/d ${formatPromoDate(promo.endDate)}';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Ikon 3D sesuai hadiah promo.
|
|||
|
|
class PromoTypeIcon extends StatelessWidget {
|
|||
|
|
const PromoTypeIcon({super.key, required this.type, this.size = 32});
|
|||
|
|
|
|||
|
|
final PromoType type;
|
|||
|
|
final double size;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
return switch (type) {
|
|||
|
|
PromoType.point => WalletCurrencyIcon(
|
|||
|
|
currency: WalletCurrency.point,
|
|||
|
|
size: size,
|
|||
|
|
),
|
|||
|
|
PromoType.coin => WalletCurrencyIcon(
|
|||
|
|
currency: WalletCurrency.coin,
|
|||
|
|
size: size,
|
|||
|
|
),
|
|||
|
|
PromoType.reward => Assets.icons.enakrewardIcon.image(
|
|||
|
|
width: size,
|
|||
|
|
height: size,
|
|||
|
|
fit: BoxFit.contain,
|
|||
|
|
// Aset aslinya besar; decode seukuran tampilan saja.
|
|||
|
|
cacheWidth: (size * MediaQuery.devicePixelRatioOf(context)).round(),
|
|||
|
|
),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Label waktu promo: biru bila belum mulai, oranye bila segera berakhir.
|
|||
|
|
class PromoTimeBadge extends StatelessWidget {
|
|||
|
|
const PromoTimeBadge({super.key, required this.promo});
|
|||
|
|
|
|||
|
|
final Promo promo;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
final color = isPromoUpcoming(promo)
|
|||
|
|
? AppColor.info
|
|||
|
|
: isPromoEndingSoon(promo)
|
|||
|
|
? AppColor.warning
|
|||
|
|
: AppColor.textSecondary;
|
|||
|
|
|
|||
|
|
return Container(
|
|||
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: color.withOpacity(0.1),
|
|||
|
|
borderRadius: BorderRadius.circular(20),
|
|||
|
|
),
|
|||
|
|
child: Row(
|
|||
|
|
mainAxisSize: MainAxisSize.min,
|
|||
|
|
children: [
|
|||
|
|
Icon(Icons.schedule, size: 12, color: color),
|
|||
|
|
const SizedBox(width: 4),
|
|||
|
|
Text(
|
|||
|
|
promoTimeLabel(promo),
|
|||
|
|
style: AppStyle.xs.copyWith(
|
|||
|
|
color: color,
|
|||
|
|
fontWeight: FontWeight.w700,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|