diff --git a/lib/presentation/pages/home/widgets/feature.dart b/lib/presentation/pages/home/widgets/feature.dart index c0345ae..0df4dcd 100644 --- a/lib/presentation/pages/home/widgets/feature.dart +++ b/lib/presentation/pages/home/widgets/feature.dart @@ -45,7 +45,7 @@ class HomeFeature extends StatelessWidget { title: 'Pembelian', color: const Color(0xFF2196F3), icon: LineIcons.shoppingCart, - onTap: () {}, + onTap: () => context.router.push(PurchaseRoute()), ), HomeFeatureTile( title: 'Biaya', diff --git a/lib/presentation/pages/purchase/purchase_page.dart b/lib/presentation/pages/purchase/purchase_page.dart new file mode 100644 index 0000000..5704ee1 --- /dev/null +++ b/lib/presentation/pages/purchase/purchase_page.dart @@ -0,0 +1,370 @@ +import 'package:flutter/material.dart'; +import 'package:line_icons/line_icons.dart'; + +import 'widgets/appbar.dart'; +import 'widgets/purchase_tile.dart'; +import 'widgets/stat_card.dart'; +import 'widgets/status_chip.dart'; + +// AppColor class (sesuai dengan yang Anda berikan) +class AppColor { + // Primary Colors + static const Color primary = Color(0xFF36175e); + static const Color primaryLight = Color(0xFF5a2d85); + static const Color primaryDark = Color(0xFF1e0d35); + + // Secondary Colors + static const Color secondary = Color(0xFF4CAF50); + static const Color secondaryLight = Color(0xFF81C784); + static const Color secondaryDark = Color(0xFF388E3C); + + // Background Colors + static const Color background = Color(0xFFF8F9FA); + static const Color backgroundLight = Color(0xFFFFFFFF); + static const Color backgroundDark = Color(0xFF1A1A1A); + static const Color surface = Color(0xFFFFFFFF); + static const Color surfaceDark = Color(0xFF2D2D2D); + + // Text Colors + static const Color textPrimary = Color(0xFF212121); + static const Color textSecondary = Color(0xFF757575); + static const Color textLight = Color(0xFFBDBDBD); + static const Color textWhite = Color(0xFFFFFFFF); + + // Status Colors + static const Color success = Color(0xFF4CAF50); + static const Color error = Color(0xFFE53E3E); + static const Color warning = Color(0xFFFF9800); + static const Color info = Color(0xFF2196F3); + + // Border Colors + static const Color border = Color(0xFFE0E0E0); + static const Color borderLight = Color(0xFFF0F0F0); + static const Color borderDark = Color(0xFFBDBDBD); + + // Basic Color + static const Color white = Color(0xFFFFFFFF); + static const Color black = Color(0xFF000000); + + // Gradient Colors + static const List primaryGradient = [ + Color(0xFF36175e), + Color(0xFF5a2d85), + ]; + + static const List successGradient = [ + Color(0xFF4CAF50), + Color(0xFF81C784), + ]; + + static const List backgroundGradient = [ + Color(0xFFF5F5F5), + Color(0xFFE8E8E8), + ]; + + // Opacity Variations + static Color primaryWithOpacity(double opacity) => + primary.withOpacity(opacity); + static Color successWithOpacity(double opacity) => + success.withOpacity(opacity); + static Color errorWithOpacity(double opacity) => error.withOpacity(opacity); + static Color warningWithOpacity(double opacity) => + warning.withOpacity(opacity); +} + +// AppStyle class (sesuai dengan yang Anda berikan) +class AppStyle { + static TextStyle xs = const TextStyle( + color: AppColor.textPrimary, + fontSize: 11, + ); + static TextStyle sm = const TextStyle( + color: AppColor.textPrimary, + fontSize: 12, + ); + static TextStyle md = const TextStyle( + color: AppColor.textPrimary, + fontSize: 14, + ); + static TextStyle lg = const TextStyle( + color: AppColor.textPrimary, + fontSize: 16, + ); + static TextStyle xl = const TextStyle( + color: AppColor.textPrimary, + fontSize: 18, + ); + static TextStyle xxl = const TextStyle( + color: AppColor.textPrimary, + fontSize: 20, + ); + static TextStyle h6 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 22, + ); + static TextStyle h5 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 24, + ); + static TextStyle h4 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 26, + ); + static TextStyle h3 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 28, + ); + static TextStyle h2 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 30, + ); + static TextStyle h1 = const TextStyle( + color: AppColor.textPrimary, + fontSize: 32, + ); +} + +class PurchasePage extends StatefulWidget { + const PurchasePage({Key? key}) : super(key: key); + + @override + State createState() => _PurchasePageState(); +} + +class _PurchasePageState extends State + with TickerProviderStateMixin { + late AnimationController rotationAnimation; + late AnimationController cardAnimation; + late AnimationController floatingAnimation; + String selectedFilter = 'Semua'; + final List filterOptions = [ + 'Semua', + 'Pending', + 'Completed', + 'Cancelled', + ]; + + final List> purchaseData = [ + { + 'id': 'PO-001', + 'supplier': 'PT. Sumber Rezeki', + 'date': '15 Aug 2025', + 'total': 2500000, + 'status': 'Completed', + 'items': 15, + }, + { + 'id': 'PO-002', + 'supplier': 'CV. Maju Jaya', + 'date': '14 Aug 2025', + 'total': 1750000, + 'status': 'Pending', + 'items': 8, + }, + { + 'id': 'PO-003', + 'supplier': 'PT. Global Supply', + 'date': '13 Aug 2025', + 'total': 3200000, + 'status': 'Completed', + 'items': 22, + }, + { + 'id': 'PO-004', + 'supplier': 'UD. Berkah Mandiri', + 'date': '12 Aug 2025', + 'total': 890000, + 'status': 'Cancelled', + 'items': 5, + }, + ]; + + @override + void initState() { + super.initState(); + rotationAnimation = AnimationController( + duration: const Duration(seconds: 20), + vsync: this, + )..repeat(); + + cardAnimation = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + ); + + floatingAnimation = AnimationController( + duration: const Duration(seconds: 3), + vsync: this, + )..repeat(reverse: true); + + cardAnimation.forward(); + } + + @override + void dispose() { + rotationAnimation.dispose(); + cardAnimation.dispose(); + floatingAnimation.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColor.background, + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 120.0, + floating: false, + pinned: true, + elevation: 0, + backgroundColor: AppColor.primary, + + flexibleSpace: PurchaseAppbar( + rotationAnimation: rotationAnimation, + floatingAnimation: floatingAnimation, + ), + ), + + // Stats Cards + SliverToBoxAdapter( + child: Container( + color: AppColor.background, + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Expanded( + child: PurchaseStatCard( + title: 'Total Pembelian', + value: 'Rp 8.340.000', + icon: LineIcons.shoppingCart, + iconColor: AppColor.success, + cardAnimation: cardAnimation, + ), + ), + const SizedBox(width: 12), + Expanded( + child: PurchaseStatCard( + title: 'Pending Order', + value: '3 Orders', + icon: LineIcons.clock, + iconColor: AppColor.warning, + cardAnimation: cardAnimation, + ), + ), + ], + ), + ), + ), + + // Filter Section + SliverToBoxAdapter( + child: Container( + color: AppColor.surface, + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Riwayat Pembelian', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: AppColor.primary, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + '${purchaseData.length} Orders', + style: AppStyle.sm.copyWith( + color: AppColor.textWhite, + ), + ), + ), + ], + ), + const SizedBox(height: 12), + SizedBox( + height: 40, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: filterOptions.length, + itemBuilder: (context, index) { + final isSelected = + selectedFilter == filterOptions[index]; + return PurchaseStatusChip( + isSelected: isSelected, + text: filterOptions[index], + onSelected: (selected) { + setState(() { + selectedFilter = filterOptions[index]; + }); + }, + ); + }, + ), + ), + ], + ), + ), + ), + + // Purchase List + SliverPadding( + padding: const EdgeInsets.all(16), + sliver: SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + final purchase = purchaseData[index]; + return AnimatedBuilder( + animation: cardAnimation, + builder: (context, child) { + final delay = index * 0.1; + final animValue = (cardAnimation.value - delay).clamp( + 0.0, + 1.0, + ); + + return Transform.translate( + offset: Offset(0, 30 * (1 - animValue)), + child: Opacity( + opacity: animValue, + child: PurchaseTile(purchase: purchase, index: index), + ), + ); + }, + ); + }, childCount: purchaseData.length), + ), + ), + + // Bottom spacing for FAB + const SliverToBoxAdapter(child: SizedBox(height: 80)), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + // Navigate to create new purchase + }, + backgroundColor: AppColor.secondary, + icon: const Icon(LineIcons.plus, color: AppColor.textWhite), + label: Text( + 'Buat Pembelian', + style: AppStyle.md.copyWith( + color: AppColor.textWhite, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + } +} diff --git a/lib/presentation/pages/purchase/widgets/appbar.dart b/lib/presentation/pages/purchase/widgets/appbar.dart new file mode 100644 index 0000000..0437c0d --- /dev/null +++ b/lib/presentation/pages/purchase/widgets/appbar.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/theme/theme.dart'; + +class PurchaseAppbar extends StatelessWidget { + const PurchaseAppbar({ + super.key, + required this.rotationAnimation, + required this.floatingAnimation, + }); + + final AnimationController rotationAnimation; + final AnimationController floatingAnimation; + + @override + Widget build(BuildContext context) { + return FlexibleSpaceBar( + titlePadding: const EdgeInsets.only(left: 50, bottom: 16), + title: Text( + 'Pembelian', + style: AppStyle.xl.copyWith( + color: AppColor.textWhite, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + background: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: AppColor.primaryGradient, + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Stack( + children: [ + // Animated geometric shapes with enhanced effects + Positioned( + right: -20, + top: -20, + child: AnimatedBuilder( + animation: Listenable.merge([ + rotationAnimation, + floatingAnimation, + ]), + builder: (context, child) { + return Transform.translate( + offset: Offset( + floatingAnimation.value * 10, + floatingAnimation.value * 15, + ), + child: Transform.rotate( + angle: rotationAnimation.value * 2 * 3.14159, + child: Container( + width: 120, + height: 120, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + colors: [ + AppColor.white.withOpacity(0.15), + AppColor.white.withOpacity(0.05), + ], + ), + boxShadow: [ + BoxShadow( + color: AppColor.white.withOpacity(0.1), + blurRadius: 20, + spreadRadius: 5, + ), + ], + ), + ), + ), + ); + }, + ), + ), + Positioned( + left: -30, + bottom: -30, + child: AnimatedBuilder( + animation: Listenable.merge([ + rotationAnimation, + floatingAnimation, + ]), + builder: (context, child) { + return Transform.translate( + offset: Offset( + -floatingAnimation.value * 8, + -floatingAnimation.value * 12, + ), + child: Transform.rotate( + angle: -rotationAnimation.value * 0.5 * 3.14159, + child: Container( + width: 100, + height: 100, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + colors: [ + AppColor.white.withOpacity(0.1), + AppColor.white.withOpacity(0.02), + ], + ), + ), + ), + ), + ); + }, + ), + ), + Positioned( + right: 80, + bottom: 30, + child: AnimatedBuilder( + animation: Listenable.merge([ + rotationAnimation, + floatingAnimation, + ]), + builder: (context, child) { + return Transform.translate( + offset: Offset( + floatingAnimation.value * 5, + -floatingAnimation.value * 8, + ), + child: Transform.rotate( + angle: -rotationAnimation.value * 0.3 * 3.14159, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + gradient: LinearGradient( + colors: [ + AppColor.white.withOpacity(0.12), + AppColor.white.withOpacity(0.04), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + ), + ), + ); + }, + ), + ), + // Additional floating elements + Positioned( + left: 60, + top: 80, + child: AnimatedBuilder( + animation: floatingAnimation, + builder: (context, child) { + return Transform.translate( + offset: Offset( + floatingAnimation.value * 3, + floatingAnimation.value * 6, + ), + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.white.withOpacity(0.08), + ), + ), + ); + }, + ), + ), + Positioned( + right: 40, + top: 120, + child: AnimatedBuilder( + animation: floatingAnimation, + builder: (context, child) { + return Transform.translate( + offset: Offset( + -floatingAnimation.value * 4, + floatingAnimation.value * 7, + ), + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: AppColor.white.withOpacity(0.06), + ), + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/pages/purchase/widgets/purchase_tile.dart b/lib/presentation/pages/purchase/widgets/purchase_tile.dart new file mode 100644 index 0000000..fa22bf2 --- /dev/null +++ b/lib/presentation/pages/purchase/widgets/purchase_tile.dart @@ -0,0 +1,313 @@ +import 'package:flutter/material.dart'; +import 'package:line_icons/line_icons.dart'; + +import '../../../../common/theme/theme.dart'; + +class PurchaseTile extends StatelessWidget { + final Map purchase; + final int index; + const PurchaseTile({super.key, required this.purchase, required this.index}); + + @override + Widget build(BuildContext context) { + Color statusColor; + + switch (purchase['status']) { + case 'Completed': + statusColor = AppColor.success; + break; + case 'Pending': + statusColor = AppColor.warning; + break; + case 'Cancelled': + statusColor = AppColor.error; + break; + default: + statusColor = AppColor.textSecondary; + } + return AnimatedContainer( + duration: Duration(milliseconds: 300 + (index * 50)), + curve: Curves.easeOutCubic, + margin: const EdgeInsets.only(bottom: 16), + child: Material( + elevation: 0, + borderRadius: BorderRadius.circular(20), + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [AppColor.surface, AppColor.surface.withOpacity(0.95)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: AppColor.border.withOpacity(0.2), + width: 1, + ), + boxShadow: [ + BoxShadow( + color: AppColor.primary.withOpacity(0.08), + blurRadius: 25, + offset: const Offset(0, 10), + spreadRadius: 0, + ), + BoxShadow( + color: AppColor.black.withOpacity(0.04), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: AppColor.primaryGradient, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: AppColor.primary.withOpacity(0.3), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Text( + purchase['id'], + style: AppStyle.sm.copyWith( + fontWeight: FontWeight.w700, + color: AppColor.textWhite, + ), + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + statusColor.withOpacity(0.15), + statusColor.withOpacity(0.05), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: statusColor.withOpacity(0.2), + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: statusColor, + ), + ), + const SizedBox(width: 6), + Text( + purchase['status'], + style: AppStyle.xs.copyWith( + color: statusColor, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColor.background.withOpacity(0.5), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: AppColor.border.withOpacity(0.3), + width: 1, + ), + ), + child: Column( + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: AppColor.primary.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + LineIcons.building, + color: AppColor.primary, + size: 16, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + purchase['supplier'], + style: AppStyle.md.copyWith( + color: AppColor.textPrimary, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: AppColor.info.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + LineIcons.calendar, + color: AppColor.info, + size: 16, + ), + ), + const SizedBox(width: 12), + Text( + purchase['date'], + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + const Spacer(), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: AppColor.secondary.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + LineIcons.shoppingBag, + color: AppColor.secondary, + size: 14, + ), + const SizedBox(width: 4), + Text( + '${purchase['items']} items', + style: AppStyle.xs.copyWith( + color: AppColor.secondary, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Total Pembelian', + style: AppStyle.xs.copyWith( + color: AppColor.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + Text( + 'Rp ${purchase['total'].toString().replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]}.')}', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w800, + color: AppColor.primary, + ), + ), + ], + ), + Row( + children: [ + _buildActionButton(LineIcons.eye, AppColor.info, () {}), + const SizedBox(width: 8), + _buildActionButton( + LineIcons.edit, + AppColor.warning, + () {}, + ), + const SizedBox(width: 8), + _buildActionButton( + LineIcons.trash, + AppColor.error, + () {}, + ), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _buildActionButton( + IconData icon, + Color color, + VoidCallback onPressed, + ) { + return Container( + width: 40, + height: 40, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [color.withOpacity(0.15), color.withOpacity(0.05)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: color.withOpacity(0.2), width: 1), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(12), + onTap: onPressed, + child: Center(child: Icon(icon, color: color, size: 18)), + ), + ), + ); + } +} diff --git a/lib/presentation/pages/purchase/widgets/stat_card.dart b/lib/presentation/pages/purchase/widgets/stat_card.dart new file mode 100644 index 0000000..edb17f3 --- /dev/null +++ b/lib/presentation/pages/purchase/widgets/stat_card.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/theme/theme.dart'; + +class PurchaseStatCard extends StatelessWidget { + final String title; + final String value; + final IconData icon; + final Color iconColor; + final Animation cardAnimation; + const PurchaseStatCard({ + super.key, + required this.title, + required this.value, + required this.icon, + required this.iconColor, + required this.cardAnimation, + }); + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: cardAnimation, + builder: (context, child) { + return Transform.scale( + scale: 0.8 + (cardAnimation.value * 0.2), + child: Opacity( + opacity: cardAnimation.value, + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [AppColor.surface, AppColor.surface.withOpacity(0.9)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: AppColor.primary.withOpacity(0.1), + blurRadius: 20, + offset: const Offset(0, 8), + spreadRadius: 0, + ), + BoxShadow( + color: AppColor.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + border: Border.all( + color: AppColor.border.withOpacity(0.3), + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + iconColor.withOpacity(0.15), + iconColor.withOpacity(0.05), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: iconColor.withOpacity(0.2), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Icon(icon, color: iconColor, size: 24), + ), + ], + ), + const SizedBox(height: 16), + Text( + title, + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 6), + Text( + value, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w800, + color: AppColor.textPrimary, + height: 1.2, + ), + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/presentation/pages/purchase/widgets/status_chip.dart b/lib/presentation/pages/purchase/widgets/status_chip.dart new file mode 100644 index 0000000..91a85ab --- /dev/null +++ b/lib/presentation/pages/purchase/widgets/status_chip.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/theme/theme.dart'; + +class PurchaseStatusChip extends StatelessWidget { + final bool isSelected; + final String text; + final Function(bool) onSelected; + const PurchaseStatusChip({ + super.key, + required this.isSelected, + required this.text, + required this.onSelected, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 8), + child: FilterChip( + selected: isSelected, + label: Text( + text, + style: AppStyle.sm.copyWith( + color: isSelected ? AppColor.textWhite : AppColor.textSecondary, + ), + ), + backgroundColor: AppColor.backgroundLight, + selectedColor: AppColor.primary, + onSelected: onSelected, + ), + ); + } +} diff --git a/lib/presentation/router/app_router.dart b/lib/presentation/router/app_router.dart index 6abc5ab..13d1984 100644 --- a/lib/presentation/router/app_router.dart +++ b/lib/presentation/router/app_router.dart @@ -42,5 +42,8 @@ class AppRouter extends RootStackRouter { // Sales AutoRoute(page: SalesRoute.page), + + // Purchase page + AutoRoute(page: PurchaseRoute.page), ]; } diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index ede7c25..83010ff 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -27,27 +27,29 @@ import 'package:apskel_owner_flutter/presentation/pages/product/product_page.dar as _i8; import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart' as _i9; -import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/purchase/purchase_page.dart' as _i10; -import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart' as _i11; -import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart' as _i12; -import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart' as _i13; -import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart' as _i14; -import 'package:auto_route/auto_route.dart' as _i15; +import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart' + as _i15; +import 'package:auto_route/auto_route.dart' as _i16; /// generated route for /// [_i1.CustomerPage] -class CustomerRoute extends _i15.PageRouteInfo { - const CustomerRoute({List<_i15.PageRouteInfo>? children}) +class CustomerRoute extends _i16.PageRouteInfo { + const CustomerRoute({List<_i16.PageRouteInfo>? children}) : super(CustomerRoute.name, initialChildren: children); static const String name = 'CustomerRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i1.CustomerPage(); @@ -57,13 +59,13 @@ class CustomerRoute extends _i15.PageRouteInfo { /// generated route for /// [_i2.DailyTasksFormPage] -class DailyTasksFormRoute extends _i15.PageRouteInfo { - const DailyTasksFormRoute({List<_i15.PageRouteInfo>? children}) +class DailyTasksFormRoute extends _i16.PageRouteInfo { + const DailyTasksFormRoute({List<_i16.PageRouteInfo>? children}) : super(DailyTasksFormRoute.name, initialChildren: children); static const String name = 'DailyTasksFormRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i2.DailyTasksFormPage(); @@ -73,13 +75,13 @@ class DailyTasksFormRoute extends _i15.PageRouteInfo { /// generated route for /// [_i3.HomePage] -class HomeRoute extends _i15.PageRouteInfo { - const HomeRoute({List<_i15.PageRouteInfo>? children}) +class HomeRoute extends _i16.PageRouteInfo { + const HomeRoute({List<_i16.PageRouteInfo>? children}) : super(HomeRoute.name, initialChildren: children); static const String name = 'HomeRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i3.HomePage(); @@ -89,13 +91,13 @@ class HomeRoute extends _i15.PageRouteInfo { /// generated route for /// [_i4.InventoryPage] -class InventoryRoute extends _i15.PageRouteInfo { - const InventoryRoute({List<_i15.PageRouteInfo>? children}) +class InventoryRoute extends _i16.PageRouteInfo { + const InventoryRoute({List<_i16.PageRouteInfo>? children}) : super(InventoryRoute.name, initialChildren: children); static const String name = 'InventoryRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i4.InventoryPage(); @@ -105,13 +107,13 @@ class InventoryRoute extends _i15.PageRouteInfo { /// generated route for /// [_i5.LanguagePage] -class LanguageRoute extends _i15.PageRouteInfo { - const LanguageRoute({List<_i15.PageRouteInfo>? children}) +class LanguageRoute extends _i16.PageRouteInfo { + const LanguageRoute({List<_i16.PageRouteInfo>? children}) : super(LanguageRoute.name, initialChildren: children); static const String name = 'LanguageRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i5.LanguagePage(); @@ -121,13 +123,13 @@ class LanguageRoute extends _i15.PageRouteInfo { /// generated route for /// [_i6.LoginPage] -class LoginRoute extends _i15.PageRouteInfo { - const LoginRoute({List<_i15.PageRouteInfo>? children}) +class LoginRoute extends _i16.PageRouteInfo { + const LoginRoute({List<_i16.PageRouteInfo>? children}) : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i6.LoginPage(); @@ -137,13 +139,13 @@ class LoginRoute extends _i15.PageRouteInfo { /// generated route for /// [_i7.MainPage] -class MainRoute extends _i15.PageRouteInfo { - const MainRoute({List<_i15.PageRouteInfo>? children}) +class MainRoute extends _i16.PageRouteInfo { + const MainRoute({List<_i16.PageRouteInfo>? children}) : super(MainRoute.name, initialChildren: children); static const String name = 'MainRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i7.MainPage(); @@ -153,13 +155,13 @@ class MainRoute extends _i15.PageRouteInfo { /// generated route for /// [_i8.ProductPage] -class ProductRoute extends _i15.PageRouteInfo { - const ProductRoute({List<_i15.PageRouteInfo>? children}) +class ProductRoute extends _i16.PageRouteInfo { + const ProductRoute({List<_i16.PageRouteInfo>? children}) : super(ProductRoute.name, initialChildren: children); static const String name = 'ProductRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i8.ProductPage(); @@ -169,13 +171,13 @@ class ProductRoute extends _i15.PageRouteInfo { /// generated route for /// [_i9.ProfilePage] -class ProfileRoute extends _i15.PageRouteInfo { - const ProfileRoute({List<_i15.PageRouteInfo>? children}) +class ProfileRoute extends _i16.PageRouteInfo { + const ProfileRoute({List<_i16.PageRouteInfo>? children}) : super(ProfileRoute.name, initialChildren: children); static const String name = 'ProfileRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { return const _i9.ProfilePage(); @@ -184,81 +186,97 @@ class ProfileRoute extends _i15.PageRouteInfo { } /// generated route for -/// [_i10.ReportPage] -class ReportRoute extends _i15.PageRouteInfo { - const ReportRoute({List<_i15.PageRouteInfo>? children}) +/// [_i10.PurchasePage] +class PurchaseRoute extends _i16.PageRouteInfo { + const PurchaseRoute({List<_i16.PageRouteInfo>? children}) + : super(PurchaseRoute.name, initialChildren: children); + + static const String name = 'PurchaseRoute'; + + static _i16.PageInfo page = _i16.PageInfo( + name, + builder: (data) { + return const _i10.PurchasePage(); + }, + ); +} + +/// generated route for +/// [_i11.ReportPage] +class ReportRoute extends _i16.PageRouteInfo { + const ReportRoute({List<_i16.PageRouteInfo>? children}) : super(ReportRoute.name, initialChildren: children); static const String name = 'ReportRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { - return const _i10.ReportPage(); + return const _i11.ReportPage(); }, ); } /// generated route for -/// [_i11.SalesPage] -class SalesRoute extends _i15.PageRouteInfo { - const SalesRoute({List<_i15.PageRouteInfo>? children}) +/// [_i12.SalesPage] +class SalesRoute extends _i16.PageRouteInfo { + const SalesRoute({List<_i16.PageRouteInfo>? children}) : super(SalesRoute.name, initialChildren: children); static const String name = 'SalesRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { - return const _i11.SalesPage(); + return const _i12.SalesPage(); }, ); } /// generated route for -/// [_i12.SchedulePage] -class ScheduleRoute extends _i15.PageRouteInfo { - const ScheduleRoute({List<_i15.PageRouteInfo>? children}) +/// [_i13.SchedulePage] +class ScheduleRoute extends _i16.PageRouteInfo { + const ScheduleRoute({List<_i16.PageRouteInfo>? children}) : super(ScheduleRoute.name, initialChildren: children); static const String name = 'ScheduleRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { - return const _i12.SchedulePage(); + return const _i13.SchedulePage(); }, ); } /// generated route for -/// [_i13.SplashPage] -class SplashRoute extends _i15.PageRouteInfo { - const SplashRoute({List<_i15.PageRouteInfo>? children}) +/// [_i14.SplashPage] +class SplashRoute extends _i16.PageRouteInfo { + const SplashRoute({List<_i16.PageRouteInfo>? children}) : super(SplashRoute.name, initialChildren: children); static const String name = 'SplashRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { - return const _i13.SplashPage(); + return const _i14.SplashPage(); }, ); } /// generated route for -/// [_i14.TransactionPage] -class TransactionRoute extends _i15.PageRouteInfo { - const TransactionRoute({List<_i15.PageRouteInfo>? children}) +/// [_i15.TransactionPage] +class TransactionRoute extends _i16.PageRouteInfo { + const TransactionRoute({List<_i16.PageRouteInfo>? children}) : super(TransactionRoute.name, initialChildren: children); static const String name = 'TransactionRoute'; - static _i15.PageInfo page = _i15.PageInfo( + static _i16.PageInfo page = _i16.PageInfo( name, builder: (data) { - return const _i14.TransactionPage(); + return const _i15.TransactionPage(); }, ); }