Files
nearle_pos/lib/presentation/modules/screens/promos_view.dart
2026-07-29 11:41:53 +05:30

203 lines
6.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart';
import '../widgets/module_widgets.dart';
/// Discount rules and campaigns.
class PromosView extends StatefulWidget {
const PromosView({super.key});
@override
State<PromosView> createState() => _PromosViewState();
}
class _PromosViewState extends State<PromosView> {
final Set<String> _enabled = {'WEEKEND10', 'DAIRY5', 'FESTIVE'};
static const _campaigns = [
(
'WEEKEND10',
'Weekend Saver',
'10% off bills above ₹500',
'SatSun',
412,
AppColors.primary,
),
(
'DAIRY5',
'Dairy Days',
'5% off all dairy products',
'Ends 31 Aug',
286,
AppColors.info,
),
(
'FESTIVE',
'Festive Bonus',
'Double loyalty points',
'Ends 15 Sep',
178,
AppColors.tierGold,
),
(
'NEWCUST',
'First Purchase',
'₹50 off the first bill',
'Always on',
94,
AppColors.success,
),
];
@override
Widget build(BuildContext context) {
return ModulePage(
children: [
Wrap(
spacing: AppSpacing.lg,
runSpacing: AppSpacing.lg,
children: [
StatTile(
label: 'Active Campaigns',
value: '${_enabled.length}',
icon: Icons.campaign_rounded,
caption: 'of ${_campaigns.length} configured',
),
StatTile(
label: 'Redemptions',
value: '970',
icon: Icons.confirmation_number_rounded,
color: AppColors.info,
caption: 'this month',
),
StatTile(
label: 'Discount Given',
value: '₹48,240',
icon: Icons.local_offer_rounded,
color: AppColors.warning,
caption: '2.6% of sales',
),
StatTile(
label: 'Incremental Sales',
value: '₹2.14L',
icon: Icons.trending_up_rounded,
color: AppColors.success,
delta: '+18%',
caption: 'attributed',
),
],
),
const SizedBox(height: AppSpacing.lg),
PanelCard(
title: 'Campaigns',
subtitle: 'Toggle a rule to apply it at the till immediately',
action: FilledButton.icon(
onPressed: () {},
icon: const Icon(Icons.add_rounded, size: 18),
label: const Text('New campaign'),
style: FilledButton.styleFrom(
backgroundColor: AppColors.primary,
minimumSize: const Size(0, 40),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (final c in _campaigns)
Container(
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceAlt,
borderRadius: AppRadius.brMd,
border: Border.all(color: AppColors.border),
),
// Wrap prevents collision when the panel is narrow.
child: Wrap(
alignment: WrapAlignment.spaceBetween,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: AppSpacing.md,
runSpacing: AppSpacing.sm,
children: [
SizedBox(
width: 320,
child: Row(
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: c.$6.withValues(alpha: 0.12),
borderRadius: AppRadius.brSm,
),
child: Icon(Icons.sell_rounded,
size: 18, color: c.$6),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
c.$2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
c.$3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12.5,
color: AppColors.textSecondary,
),
),
],
),
),
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
TagChip(c.$1, color: c.$6),
const SizedBox(width: AppSpacing.sm),
TagChip(c.$4, color: AppColors.textSecondary),
const SizedBox(width: AppSpacing.sm),
Text(
'${c.$5} used',
style: const TextStyle(
fontSize: 12,
color: AppColors.textTertiary,
),
),
const SizedBox(width: AppSpacing.sm),
Switch(
value: _enabled.contains(c.$1),
onChanged: (v) => setState(() {
if (v) {
_enabled.add(c.$1);
} else {
_enabled.remove(c.$1);
}
}),
),
],
),
],
),
),
],
),
),
],
);
}
}