feat: add payment card grouping and analysis

This commit is contained in:
JiWoong Sul
2025-11-14 16:53:41 +09:00
parent cba7d082bd
commit 132ae758de
40 changed files with 2846 additions and 522 deletions

View File

@@ -3,7 +3,12 @@ import 'package:provider/provider.dart';
import '../../models/subscription_model.dart';
import '../../controllers/detail_screen_controller.dart';
import '../../providers/locale_provider.dart';
import '../../providers/payment_card_provider.dart';
import '../../services/currency_util.dart';
import '../../utils/payment_card_utils.dart';
import '../../models/payment_card_model.dart';
import '../payment_card/payment_card_form_sheet.dart';
import '../../routes/app_routes.dart';
import '../website_icon.dart';
import '../../l10n/app_localizations.dart';
@@ -30,6 +35,10 @@ class DetailHeaderSection extends StatelessWidget {
return Consumer<DetailScreenController>(
builder: (context, controller, child) {
final baseColor = controller.getCardColor();
final paymentCardProvider = context.watch<PaymentCardProvider>();
final paymentCard = paymentCardProvider.getCardById(
controller.selectedPaymentCardId ?? subscription.paymentCardId,
);
return Container(
height: 320,
@@ -172,6 +181,11 @@ class DetailHeaderSection extends StatelessWidget {
.withValues(alpha: 0.8),
),
),
const SizedBox(height: 12),
_buildPaymentCardChip(
context,
paymentCard,
),
],
),
),
@@ -268,6 +282,104 @@ class DetailHeaderSection extends StatelessWidget {
return cycle;
}
}
Widget _buildPaymentCardChip(
BuildContext context,
PaymentCardModel? card,
) {
final loc = AppLocalizations.of(context);
if (card == null) {
return GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(AppRoutes.paymentCardManagement);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: Colors.white.withValues(alpha: 0.2),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.credit_card_off_rounded,
color: Colors.white,
size: 16,
),
const SizedBox(width: 8),
Text(
loc.paymentCardUnassigned,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
const SizedBox(width: 8),
Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.white.withValues(alpha: 0.7),
size: 14,
),
],
),
),
);
}
final color = PaymentCardUtils.colorFromHex(card.colorHex);
final icon = PaymentCardUtils.iconForName(card.iconName);
return GestureDetector(
onTap: () async {
await PaymentCardFormSheet.show(context, card: card);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 9),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: color.withValues(alpha: 0.5),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircleAvatar(
radius: 14,
backgroundColor: Colors.white,
child: Icon(
icon,
size: 16,
color: color,
),
),
const SizedBox(width: 10),
Text(
'${card.issuerName} · ****${card.last4}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
const SizedBox(width: 8),
Icon(
Icons.edit_rounded,
size: 16,
color: Colors.white.withValues(alpha: 0.8),
),
],
),
),
);
}
}
/// 정보 표시 컬럼