Files
enaklo-flutter/lib/presentation/pages/reward/widgets/voucher_card.dart
T
efrilm 38850b281f
Build & Deploy iOS to TestFlight / build-and-deploy (push) Canceled after 0s
feat: integrate enakgame and voucher
2026-10-10 02:33:16 +07:00

125 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../../domain/wallet/wallet.dart';
import '../../wallet/wallet_ui.dart';
import '../reward_ui.dart';
import 'ticket_card.dart';
/// Voucher di katalog (§9.1).
class VoucherCard extends StatelessWidget {
const VoucherCard({
super.key,
required this.voucher,
required this.canAfford,
required this.onTap,
});
final Voucher voucher;
/// Saldo EnakPoint cukup untuk [Voucher.pointCost].
final bool canAfford;
final VoidCallback onTap;
bool get _canRedeem => canAfford && !voucher.isOutOfStock;
@override
Widget build(BuildContext context) {
final faceValue = voucherFaceValueLabel(voucher.type, voucher.faceValue);
return TicketCard(
active: _canRedeem,
onTap: onTap,
stub: TicketStub(
image: VoucherImage(
url: voucher.imageUrl,
size: 44,
dimmed: voucher.isOutOfStock,
),
pointCost: voucher.pointCost.thousandFormat,
active: _canRedeem,
),
body: Padding(
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
voucherTypeLabel(voucher.type, voucher.faceValue).toUpperCase(),
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w700,
letterSpacing: 0.6,
),
),
const SizedBox(height: 4),
Text(
voucher.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: AppStyle.lg.copyWith(fontWeight: FontWeight.w700),
),
if (faceValue != null)
Text(
faceValue,
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
const SizedBox(height: 10),
Row(
children: [
Expanded(child: _buildFootnote()),
const SizedBox(width: 8),
_buildAction(),
],
),
],
),
),
);
}
Widget _buildFootnote() {
final parts = [
if (voucher.validUntil != null)
's/d ${formatWibDate(voucher.validUntil!)}',
if (voucher.available != null && !voucher.isOutOfStock)
'sisa ${voucher.available!.thousandFormat}',
];
if (parts.isEmpty) return const SizedBox.shrink();
return Text(
parts.join(' · '),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
);
}
Widget _buildAction() {
final (String label, bool enabled) = voucher.isOutOfStock
? ('Habis', false)
: canAfford
? ('Tukar', true)
: ('Poin kurang', false);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
decoration: BoxDecoration(
color: enabled ? AppColor.primary : AppColor.background,
borderRadius: BorderRadius.circular(20),
),
child: Text(
label,
style: AppStyle.sm.copyWith(
color: enabled ? AppColor.white : AppColor.textSecondary,
fontWeight: FontWeight.w700,
),
),
);
}
}