Compare commits

...

5 Commits

Author SHA1 Message Date
efrilm
06cc7e3781 feat: merchant page 2025-08-29 21:02:57 +07:00
efrilm
88c3cebd31 feat: update 2025-08-29 20:50:03 +07:00
efrilm
c13e3c598e feat: update 2025-08-29 20:48:40 +07:00
efrilm
936875c30a feat: order card 2025-08-29 20:45:42 +07:00
efrilm
0afb683045 feat: draw card 2025-08-29 20:43:03 +07:00
9 changed files with 209 additions and 796 deletions

View File

@ -16,19 +16,6 @@ class ThemeApp {
fontFamily: FontFamily.quicksand, fontFamily: FontFamily.quicksand,
primaryColor: AppColor.primary, primaryColor: AppColor.primary,
scaffoldBackgroundColor: AppColor.white, scaffoldBackgroundColor: AppColor.white,
textTheme: TextTheme(
bodySmall: AppStyle.xs,
bodyMedium: AppStyle.md,
bodyLarge: AppStyle.lg,
labelSmall: AppStyle.sm,
labelLarge: AppStyle.xl,
headlineSmall: AppStyle.h6,
headlineMedium: AppStyle.h5,
headlineLarge: AppStyle.h4,
displaySmall: AppStyle.h3,
displayMedium: AppStyle.h2,
displayLarge: AppStyle.h1,
),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: AppColor.white, backgroundColor: AppColor.white,
foregroundColor: AppColor.textPrimary, foregroundColor: AppColor.textPrimary,

View File

@ -107,10 +107,6 @@ class _DrawPageState extends State<DrawPage> {
}).toList(); }).toList();
} }
bool _isUserEntered(String drawId) {
return userEntries.any((entry) => entry.drawId == drawId);
}
String _getTimeRemaining(DateTime targetDate) { String _getTimeRemaining(DateTime targetDate) {
final now = DateTime.now(); final now = DateTime.now();
final difference = targetDate.difference(now); final difference = targetDate.difference(now);
@ -218,389 +214,82 @@ class _DrawPageState extends State<DrawPage> {
} }
Widget _buildSimpleDrawCard(DrawEvent draw) { Widget _buildSimpleDrawCard(DrawEvent draw) {
_isUserEntered(draw.id);
final timeRemaining = _getTimeRemaining(draw.drawDate); final timeRemaining = _getTimeRemaining(draw.drawDate);
return GestureDetector( return GestureDetector(
onTap: () => context.router.push(DrawDetailRoute(drawEvent: draw)), onTap: () => context.router.push(DrawDetailRoute(drawEvent: draw)),
child: Container( child: Container(
margin: EdgeInsets.only(bottom: 16), margin: EdgeInsets.only(bottom: 8),
decoration: BoxDecoration( padding: EdgeInsets.all(12),
color: AppColor.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.08),
blurRadius: 12,
offset: Offset(0, 4),
),
],
),
child: Column(
children: [
// Header with gradient background
Container(
height: 160,
width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [draw.primaryColor, draw.primaryColor.withOpacity(0.8)],
draw.primaryColor,
draw.primaryColor.withOpacity(0.8),
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
child: Stack(
children: [
// Background pattern (coins and gold bar)
Positioned(
right: 20,
top: 20,
child: Opacity(
opacity: 0.3,
child: Column(
children: [
Row(
children: [
_buildCoin(),
SizedBox(width: 8),
_buildCoin(),
],
),
SizedBox(height: 8),
_buildGoldBar(),
],
),
),
),
// Content
Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (draw.isActive)
Container(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: AppColor.success,
borderRadius: BorderRadius.circular(4),
),
child: Text(
"AKTIF",
style: AppStyle.xs.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 8),
Text(
"MAKAN\nDAPAT",
style: AppStyle.h5.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
height: 0.9,
),
),
SizedBox(height: 4),
Text(
draw.description,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite,
),
),
],
),
),
],
),
),
// Card content
Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Prize info
Row(
children: [
Text(draw.icon, style: AppStyle.h5),
SizedBox(width: 8),
Text(
draw.prize,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
],
),
SizedBox(height: 16),
// Stats row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Text(
"-",
style: AppStyle.h5.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
Text(
"Peserta",
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
Column(
children: [
Text(
"${draw.hadiah}",
style: AppStyle.h5.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
Text(
"Hadiah",
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
Column(
children: [
Text(
"0",
style: AppStyle.h5.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.error,
),
),
Text(
"Voucher Anda",
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
],
),
SizedBox(height: 16),
// Timer
if (draw.isActive)
Row(
children: [
Icon(
Icons.access_time,
color: AppColor.error,
size: 16,
),
SizedBox(width: 4),
Text(
"Berakhir dalam:",
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
),
),
Spacer(),
Text(
timeRemaining,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.error,
),
),
],
),
SizedBox(height: 12),
// Spending requirement
GestureDetector(
onTap: () => _showSpendingInfo(draw),
child: Container(
padding: EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
decoration: BoxDecoration(
color: AppColor.backgroundLight,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all( boxShadow: [
color: AppColor.borderLight, BoxShadow(
width: 1, color: AppColor.black.withOpacity(0.08),
blurRadius: 8,
offset: Offset(0, 2),
), ),
],
), ),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: Text( child: Column(
"Belanja min. Rp ${_formatCurrency(draw.minSpending)} untuk berpartisipasi",
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
),
Icon(
Icons.chevron_right,
color: AppColor.textSecondary,
size: 16,
),
],
),
),
),
],
),
),
],
),
),
);
}
Widget _buildCoin() {
return Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: AppColor.warning,
shape: BoxShape.circle,
border: Border.all(color: Colors.yellow, width: 1),
),
child: Center(
child: Text(
"₹",
style: AppStyle.xs.copyWith(
color: Colors.orange[800],
fontWeight: FontWeight.bold,
),
),
),
);
}
Widget _buildGoldBar() {
return Container(
width: 30,
height: 15,
decoration: BoxDecoration(
color: AppColor.warning,
borderRadius: BorderRadius.circular(2),
border: Border.all(color: Colors.yellow, width: 1),
),
child: Center(
child: Text(
"GOLD",
style: TextStyle(
color: Colors.orange[800],
fontSize: 6,
fontWeight: FontWeight.bold,
),
),
),
);
}
String _formatCurrency(int amount) {
return amount.toString().replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]}.',
);
}
void _showSpendingInfo(DrawEvent draw) {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: AppColor.surface,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Text(
"Syarat Partisipasi",
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Name
Text( Text(
"Untuk mengikuti undian ${draw.name}, Anda perlu:", draw.name,
style: AppStyle.md.copyWith(color: AppColor.textPrimary), style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textWhite,
), ),
SizedBox(height: 12), ),
Row( SizedBox(height: 4),
// Description
Text(
draw.description,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.9),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 6),
// Date
Text(
draw.isActive ? "Berakhir: $timeRemaining" : "Selesai",
style: AppStyle.xs.copyWith(
color: AppColor.textWhite.withOpacity(0.8),
fontWeight: FontWeight.w500,
),
),
],
),
),
// Price
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Icon(Icons.shopping_cart, color: draw.primaryColor, size: 20), Text(draw.icon, style: AppStyle.lg),
SizedBox(width: 8), SizedBox(height: 2),
Expanded( Text(
child: Text( draw.prize,
"Belanja minimum Rp ${_formatCurrency(draw.minSpending)}", style: AppStyle.sm.copyWith(
style: AppStyle.md.copyWith( fontWeight: FontWeight.bold,
fontWeight: FontWeight.w600, color: AppColor.textWhite,
color: AppColor.textPrimary,
),
),
),
],
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.schedule, color: draw.primaryColor, size: 20),
SizedBox(width: 8),
Expanded(
child: Text(
"Sebelum ${_formatDateTime(draw.drawDate)}",
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
), ),
), ),
], ],
), ),
], ],
), ),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
"Mengerti",
style: AppStyle.md.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w600,
),
),
),
],
), ),
); );
} }
String _formatDateTime(DateTime date) {
return "${date.day}/${date.month}/${date.year} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}";
}
} }

View File

@ -40,7 +40,7 @@ class _HomePageState extends State<HomePage> {
_buildHeaderSection(), _buildHeaderSection(),
const SizedBox(height: 70), const SizedBox(height: 70),
HomeFeatureSection(), HomeFeatureSection(),
HomeLotteryBanner(), HomeLotteryBanner(onTap: () => context.router.push(DrawRoute())),
HomePopularMerchantSection(), HomePopularMerchantSection(),
], ],
), ),

View File

@ -8,7 +8,7 @@ class HomeLotteryBanner extends StatefulWidget {
this.title = "🎰 UNDIAN BERHADIAH", this.title = "🎰 UNDIAN BERHADIAH",
this.subtitle = "Kumpulkan voucher untuk menang hadiah menarik!", this.subtitle = "Kumpulkan voucher untuk menang hadiah menarik!",
this.showAnimation = true, this.showAnimation = true,
this.actionText = "MAIN SEKARANG", this.actionText = "IKUTI SEKARANG",
}); });
final VoidCallback? onTap; final VoidCallback? onTap;

View File

@ -144,7 +144,6 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColor.background,
appBar: _buildAppBar(), appBar: _buildAppBar(),
body: Column( body: Column(
children: [ children: [
@ -161,6 +160,7 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
return AppBar( return AppBar(
elevation: 0, elevation: 0,
backgroundColor: AppColor.white, backgroundColor: AppColor.white,
automaticallyImplyLeading: false,
title: Text('Pesanan'), title: Text('Pesanan'),
actions: [ actions: [
IconButton( IconButton(

View File

@ -15,64 +15,32 @@ class OrderCard extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: () => context.router.push(OrderDetailRoute(order: order)), onTap: () => context.router.push(OrderDetailRoute(order: order)),
child: Container( child: Container(
margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.white, border: Border(
borderRadius: BorderRadius.circular(16), bottom: BorderSide(
boxShadow: [ color: AppColor.border.withOpacity(0.3),
BoxShadow( width: 1,
color: AppColor.black.withOpacity(0.06),
blurRadius: 16,
offset: const Offset(0, 3),
), ),
],
), ),
child: Padding( ),
padding: const EdgeInsets.all(18), child: Row(
child: Column(
children: [
_buildHeader(),
const SizedBox(height: 16),
_buildContent(),
const SizedBox(height: 16),
_buildFooter(),
],
),
),
),
);
}
Widget _buildHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
// Left side - Order info
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( // Order ID
children: [ Text(
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: AppColor.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Text(
order.id, order.id,
style: AppStyle.sm.copyWith( style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColor.primary, color: AppColor.textPrimary,
), ),
), ),
), const SizedBox(height: 4),
], // Order Date
),
const SizedBox(height: 6),
Text( Text(
DateFormat('dd MMM yyyy • HH:mm').format(order.orderDate), DateFormat('dd MMM yyyy • HH:mm').format(order.orderDate),
style: AppStyle.sm.copyWith(color: AppColor.textSecondary), style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
@ -80,206 +48,47 @@ class OrderCard extends StatelessWidget {
], ],
), ),
), ),
_buildStatusChip(),
],
);
}
Widget _buildStatusChip() { // Right side - Status and Total
return Container( Column(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Status
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _getStatusColor().withOpacity(0.1), color: _getStatusColor().withOpacity(0.1),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(12),
border: Border.all(color: _getStatusColor().withOpacity(0.2), width: 1), border: Border.all(
color: _getStatusColor().withOpacity(0.2),
width: 1,
), ),
child: Row( ),
mainAxisSize: MainAxisSize.min, child: Text(
children: [
Icon(_getStatusIcon(), size: 12, color: _getStatusColor()),
const SizedBox(width: 6),
Text(
_getStatusText(), _getStatusText(),
style: AppStyle.sm.copyWith( style: AppStyle.xs.copyWith(
color: _getStatusColor(), color: _getStatusColor(),
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
],
),
);
}
Widget _buildContent() {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColor.background,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.restaurant_menu_outlined,
size: 16,
color: AppColor.textSecondary,
),
const SizedBox(width: 6),
Text(
'${order.items.length} item pesanan',
style: AppStyle.sm.copyWith(
fontWeight: FontWeight.w500,
color: AppColor.textSecondary,
),
),
],
),
const SizedBox(height: 10),
...order.items
.take(3)
.map(
(item) => Container(
margin: const EdgeInsets.only(bottom: 6),
child: Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: AppColor.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: Text(
'${item.quantity}',
style: AppStyle.xs.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.primary,
),
),
),
),
const SizedBox(width: 10),
Expanded(
child: Text(
item.name,
style: AppStyle.sm.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
'Rp ${_formatCurrency(item.price * item.quantity)}',
style: AppStyle.sm.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
if (order.items.length > 3) ...[
Container(
margin: const EdgeInsets.only(top: 4),
child: Text(
'+${order.items.length - 3} item lainnya',
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontStyle: FontStyle.italic,
),
),
),
],
if (order.notes != null) ...[
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColor.warning.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.warning.withOpacity(0.2)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.sticky_note_2_outlined,
size: 14,
color: AppColor.warning,
),
const SizedBox(width: 6),
Expanded(
child: Text(
order.notes!,
style: AppStyle.xs.copyWith(
color: AppColor.textPrimary,
height: 1.3,
),
),
),
],
),
),
],
],
),
);
}
Widget _buildFooter() {
return Column(
children: [
Container(
height: 1,
width: double.infinity,
color: AppColor.border.withOpacity(0.3),
),
const SizedBox(height: 12),
Row(
children: [
Icon(
order.address != null
? Icons.location_on_outlined
: Icons.store_outlined,
size: 16,
color: AppColor.textSecondary,
),
const SizedBox(width: 6),
Expanded(
child: Text(
order.address ?? 'Ambil di tempat',
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'Total',
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
), ),
const SizedBox(height: 6),
// Total Amount
Text( Text(
'Rp ${_formatCurrency(order.totalAmount)}', 'Rp ${_formatCurrency(order.totalAmount)}',
style: AppStyle.lg.copyWith( style: AppStyle.md.copyWith(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: AppColor.primary, color: AppColor.textPrimary,
), ),
), ),
], ],
), ),
], ],
), ),
], ),
); );
} }
@ -309,19 +118,6 @@ class OrderCard extends StatelessWidget {
} }
} }
IconData _getStatusIcon() {
switch (order.status) {
case OrderStatus.pending:
return Icons.schedule;
case OrderStatus.processing:
return Icons.hourglass_empty;
case OrderStatus.completed:
return Icons.check_circle;
case OrderStatus.cancelled:
return Icons.cancel;
}
}
String _formatCurrency(double amount) { String _formatCurrency(double amount) {
final formatter = NumberFormat('#,###'); final formatter = NumberFormat('#,###');
return formatter.format(amount); return formatter.format(amount);

View File

@ -1,6 +1,5 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../../common/theme/theme.dart'; import '../../../../../common/theme/theme.dart';
@ -12,7 +11,7 @@ class ProfilePage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColor.background, backgroundColor: AppColor.background,
appBar: AppBar(title: Text('Akun')), appBar: AppBar(title: Text('Profil'), automaticallyImplyLeading: false),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
@ -165,61 +164,6 @@ class ProfilePage extends StatelessWidget {
], ],
), ),
), ),
const SizedBox(height: 20),
// Share the Sip Card
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.backgroundLight,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Row(
children: [
// Share Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColor.success.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.share,
color: AppColor.success,
size: 20,
),
),
const SizedBox(width: 12),
// Share Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Share the Sip',
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
Text(
'Bagikan kode referral, dapatkan hadiah',
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: AppColor.textSecondary,
size: 14,
),
],
),
),
], ],
), ),
), ),
@ -280,52 +224,6 @@ class ProfilePage extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
// Social Media Section
Container(
color: AppColor.white,
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Media Sosial',
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildSocialButton(
icon: Icons.camera_alt,
color: Colors.purple,
onTap: () => _launchSocialMedia('instagram'),
),
_buildSocialButton(
icon: Icons.facebook,
color: Colors.blue,
onTap: () => _launchSocialMedia('facebook'),
),
_buildSocialButton(
icon: Icons.play_arrow,
color: Colors.red,
onTap: () => _launchSocialMedia('youtube'),
),
_buildSocialButton(
icon: Icons.close,
color: Colors.black,
onTap: () => _launchSocialMedia('twitter'),
),
],
),
],
),
),
const SizedBox(height: 8),
// Customer Service Section // Customer Service Section
Container( Container(
color: AppColor.white, color: AppColor.white,
@ -467,49 +365,12 @@ class ProfilePage extends StatelessWidget {
); );
} }
Widget _buildSocialButton({
required IconData icon,
required Color color,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
child: Icon(icon, color: AppColor.white, size: 24),
),
);
}
void _launchSocialMedia(String platform) async {
String url = '';
switch (platform) {
case 'instagram':
url = 'https://instagram.com/';
break;
case 'facebook':
url = 'https://facebook.com/';
break;
case 'youtube':
url = 'https://youtube.com/';
break;
case 'twitter':
url = 'https://twitter.com/';
break;
}
if (await canLaunch(url)) {
await launch(url);
}
}
void _showLogoutDialog(BuildContext context) { void _showLogoutDialog(BuildContext context) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
backgroundColor: AppColor.white,
title: Text( title: Text(
'Logout', 'Logout',
style: AppStyle.lg.copyWith(fontWeight: FontWeight.w600), style: AppStyle.lg.copyWith(fontWeight: FontWeight.w600),

View File

@ -19,11 +19,14 @@ class _MerchantPageState extends State<MerchantPage> {
final TextEditingController _searchController = TextEditingController(); final TextEditingController _searchController = TextEditingController();
final List<MerchantModel> _allMerchants = _generateMockMerchants(); final List<MerchantModel> _allMerchants = _generateMockMerchants();
List<MerchantModel> _filteredMerchants = []; List<MerchantModel> _filteredMerchants = [];
String? _selectedCategory;
late List<String> _categories;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_filteredMerchants = _allMerchants; _filteredMerchants = _allMerchants;
_categories = _getAllCategories();
} }
@override @override
@ -32,12 +35,29 @@ class _MerchantPageState extends State<MerchantPage> {
super.dispose(); super.dispose();
} }
List<String> _getAllCategories() {
final categories = _allMerchants
.map((merchant) => merchant.category)
.toSet()
.toList();
categories.sort();
return categories;
}
void _filterMerchants(String query) { void _filterMerchants(String query) {
setState(() { setState(() {
if (query.isEmpty) { var merchants = _allMerchants;
_filteredMerchants = _allMerchants;
} else { // Filter by category first
_filteredMerchants = _allMerchants if (_selectedCategory != null) {
merchants = merchants
.where((merchant) => merchant.category == _selectedCategory)
.toList();
}
// Then filter by search query
if (query.isNotEmpty) {
merchants = merchants
.where( .where(
(merchant) => (merchant) =>
merchant.name.toLowerCase().contains(query.toLowerCase()) || merchant.name.toLowerCase().contains(query.toLowerCase()) ||
@ -45,26 +65,98 @@ class _MerchantPageState extends State<MerchantPage> {
) )
.toList(); .toList();
} }
_filteredMerchants = merchants;
}); });
} }
void _onCategorySelected(String? category) {
setState(() {
_selectedCategory = category;
});
_filterMerchants(_searchController.text);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColor.background, backgroundColor: AppColor.background,
appBar: AppBar( appBar: AppBar(
title: Text('Merchants'), title: const Text('Merchants'),
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: const Size.fromHeight(70), preferredSize: const Size.fromHeight(
child: SearchTextField( 130,
), // Increased height for filter chips
child: Column(
children: [
// Search Field
SearchTextField(
hintText: 'Search merchants...', hintText: 'Search merchants...',
prefixIcon: Icons.search, prefixIcon: Icons.search,
controller: _searchController, controller: _searchController,
// onChanged: _filterMerchants,
onClear: () { onClear: () {
_searchController.clear(); _searchController.clear();
_filterMerchants(''); _filterMerchants('');
}, },
), ),
const SizedBox(height: 12),
// Category Filter Chips
// Category Filter Chips - Updated with AppColor and opacity 1
Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
// "All" filter chip
Padding(
padding: const EdgeInsets.only(right: 8),
child: FilterChip(
label: const Text('All'),
selected: _selectedCategory == null,
onSelected: (selected) {
_onCategorySelected(null);
},
selectedColor: AppColor.primary,
backgroundColor: AppColor.white,
checkmarkColor: AppColor.white,
labelStyle: TextStyle(
color: _selectedCategory == null
? AppColor.white
: AppColor.primary,
),
side: BorderSide(color: AppColor.primary, width: 1),
),
),
// Category filter chips
..._categories.map(
(category) => Padding(
padding: const EdgeInsets.only(right: 8),
child: FilterChip(
label: Text(category),
selected: _selectedCategory == category,
onSelected: (selected) {
_onCategorySelected(selected ? category : null);
},
selectedColor: AppColor.primary,
backgroundColor: AppColor.white,
checkmarkColor: AppColor.white,
labelStyle: TextStyle(
color: _selectedCategory == category
? AppColor.white
: AppColor.primary,
),
side: BorderSide(color: AppColor.primary, width: 1),
),
),
),
],
),
),
const SizedBox(height: 8),
],
),
), ),
), ),
body: _filteredMerchants.isEmpty body: _filteredMerchants.isEmpty

View File

@ -47,19 +47,7 @@ class _OrderDetailPageState extends State<OrderDetailPage> {
PreferredSizeWidget _buildAppBar() { PreferredSizeWidget _buildAppBar() {
return AppBar( return AppBar(
elevation: 0, title: Text('Detail Pesanan'),
backgroundColor: AppColor.white,
leading: IconButton(
onPressed: () => context.router.back(),
icon: const Icon(Icons.arrow_back, color: AppColor.textPrimary),
),
title: Text(
'Detail Pesanan',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
actions: [ actions: [
IconButton( IconButton(
onPressed: _shareOrder, onPressed: _shareOrder,