Files

168 lines
6.0 KiB
Dart
Raw Permalink Normal View History

2025-10-25 00:13:43 +07:00
import 'package:flutter/material.dart';
2025-10-25 03:03:44 +07:00
import 'package:flutter_bloc/flutter_bloc.dart';
2025-10-25 00:13:43 +07:00
2025-10-25 03:03:44 +07:00
import '../../../application/checkout/checkout_form/checkout_form_bloc.dart';
2025-10-25 00:13:43 +07:00
import '../../../common/extension/extension.dart';
import '../../../common/theme/theme.dart';
import '../../../domain/product/product.dart';
2025-10-25 00:19:51 +07:00
import '../dialog/dialog.dart';
2025-10-25 00:13:43 +07:00
import '../image/image.dart';
import '../spaces/space.dart';
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard({super.key, required this.product});
@override
Widget build(BuildContext context) {
return GestureDetector(
2025-10-25 00:19:51 +07:00
onTap: () {
2026-09-06 23:03:31 +07:00
if (product.isActive != true) return;
if (product.variants.isNotEmpty) {
showDialog(
context: context,
builder: (context) => VariantDialog(product: product),
);
return;
2025-10-25 00:19:51 +07:00
}
2026-09-06 23:03:31 +07:00
// Produk timbangan tidak bisa langsung masuk keranjang — kasir harus
// menimbang dulu, dan tiap penimbangan jadi barisnya sendiri.
if (product.isSoldByWeight) {
final bloc = context.read<CheckoutFormBloc>();
showDialog(
context: context,
builder: (context) => WeightInputDialog(
product: product,
onSubmit: (weight) => bloc.add(
CheckoutFormEvent.addWeightedItem(product, null, weight),
),
),
);
return;
}
context.read<CheckoutFormBloc>().add(
CheckoutFormEvent.addItem(product, null),
);
2025-10-25 00:19:51 +07:00
},
2025-10-25 00:13:43 +07:00
child: Container(
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: AppColor.disabled),
),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ClipRRect(
2026-03-01 20:02:12 +07:00
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
2025-10-25 00:13:43 +07:00
child: product.imageUrl == ""
? Container(
width: double.infinity,
height: 120,
decoration: BoxDecoration(
color: AppColor.disabled.withOpacity(0.4),
),
child: const Icon(
Icons.image,
color: AppColor.textSecondary,
),
)
: AppNetworkImage(
url: product.imageUrl,
fit: BoxFit.fill,
width: double.infinity,
height: 140,
),
),
const Spacer(),
Text(
product.name,
style: AppStyle.md.copyWith(fontWeight: FontWeight.w700),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
2026-03-01 20:02:12 +07:00
const SpaceHeight(4),
2025-10-25 00:13:43 +07:00
Align(
alignment: Alignment.center,
child: Text(
2026-09-06 23:03:31 +07:00
product.isSoldByWeight
? '${product.price.currencyFormatRp} ${product.priceUnitLabel}'
.trim()
: product.price.currencyFormatRp,
textAlign: TextAlign.center,
2025-10-25 00:13:43 +07:00
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
2026-03-01 20:02:12 +07:00
const SpaceHeight(4),
2025-10-25 00:13:43 +07:00
],
),
),
2025-10-25 03:03:44 +07:00
BlocBuilder<CheckoutFormBloc, CheckoutFormState>(
builder: (context, state) {
2026-09-06 23:03:31 +07:00
final lines = state.items
2025-10-25 03:03:44 +07:00
.where((item) => item.product.id == product.id)
2026-09-06 23:03:31 +07:00
.toList();
if (lines.isEmpty) return const SizedBox.shrink();
// Untuk produk timbangan, jumlah baris tidak berarti apa-apa
// (selalu 1 per penimbangan) — yang informatif adalah total
// berat yang sudah masuk keranjang.
final label = product.isSoldByWeight
? '${lines.fold<double>(0, (sum, item) => sum + (item.weight ?? 0)).weightFormat} ${product.unitLabel}'
.trim()
: lines
.fold<int>(0, (sum, item) => sum + item.quantity)
.toString();
2025-10-25 03:03:44 +07:00
return Positioned(
top: 4,
right: 4,
child: Container(
2026-09-06 23:03:31 +07:00
constraints: const BoxConstraints(minWidth: 40),
2025-10-25 03:03:44 +07:00
height: 40,
2026-09-06 23:03:31 +07:00
padding: const EdgeInsets.symmetric(horizontal: 6),
2025-10-25 03:03:44 +07:00
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.0)),
color: AppColor.primary,
),
child: Center(
child: Text(
2026-09-06 23:03:31 +07:00
label,
style: TextStyle(
2025-10-25 03:03:44 +07:00
color: Colors.white,
2026-09-06 23:03:31 +07:00
fontSize: product.isSoldByWeight ? 13 : 20,
2025-10-25 03:03:44 +07:00
fontWeight: FontWeight.bold,
),
),
2025-10-25 00:13:43 +07:00
),
),
2025-10-25 03:03:44 +07:00
);
},
2025-10-25 00:13:43 +07:00
),
if (product.isActive == false)
Container(
width: double.infinity,
decoration: BoxDecoration(
2026-03-01 20:02:12 +07:00
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
2025-10-25 00:13:43 +07:00
color: AppColor.disabled.withOpacity(0.5),
),
),
],
),
),
);
}
}