updates finance screen

This commit is contained in:
Vaibhav Surve 2025-11-05 17:26:32 +05:30
parent a2f5414240
commit 0b0c072473

View File

@ -3,6 +3,7 @@ import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:marco/controller/project_controller.dart'; import 'package:marco/controller/project_controller.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/widgets/my_card.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text.dart';
@ -106,364 +107,63 @@ class _FinanceScreenState extends State<FinanceScreen>
opacity: _fadeAnimation, opacity: _fadeAnimation,
child: SingleChildScrollView( child: SingleChildScrollView(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: _buildFinanceModulesCompact(),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildWelcomeSection(),
MySpacing.height(24),
_buildFinanceModules(),
MySpacing.height(24),
_buildQuickStatsSection(),
],
),
), ),
), ),
); );
} }
Widget _buildWelcomeSection() { // --- Finance Modules (Single Row, Equally Spaced) ---
final projectSelected = projectController.selectedProject != null; Widget _buildFinanceModulesCompact() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
contentTheme.primary.withValues(alpha: 0.1),
contentTheme.info.withValues(alpha: 0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: contentTheme.primary.withValues(alpha: 0.2),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: contentTheme.primary.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
LucideIcons.landmark,
color: contentTheme.primary,
size: 24,
),
),
MySpacing.width(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleMedium(
'Financial Management',
fontWeight: 700,
color: Colors.black87,
),
MySpacing.height(2),
MyText.bodySmall(
projectSelected
? 'Manage your project finances'
: 'Select a project to get started',
color: Colors.grey[600],
),
],
),
),
],
),
if (!projectSelected) ...[
MySpacing.height(12),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
),
child: Row(
children: [
Icon(
LucideIcons.badge_alert,
size: 16,
color: Colors.orange[700],
),
MySpacing.width(8),
Expanded(
child: MyText.bodySmall(
'Please select a project to access finance modules',
color: Colors.orange[700],
fontWeight: 500,
),
),
],
),
),
],
],
),
);
}
Widget _buildFinanceModules() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleMedium(
'Finance Modules',
fontWeight: 700,
color: Colors.black87,
),
MySpacing.height(4),
MyText.bodySmall(
'Select a module to manage',
color: Colors.grey[600],
),
MySpacing.height(16),
_buildModuleGrid(),
],
);
}
Widget _buildModuleGrid() {
final stats = [ final stats = [
_FinanceStatItem( _FinanceStatItem(LucideIcons.badge_dollar_sign, "Expense",
LucideIcons.badge_dollar_sign, contentTheme.info, "/dashboard/expense-main-page"),
"Expense", _FinanceStatItem(LucideIcons.receipt_text, "Payment Request",
"Track and manage expenses", contentTheme.primary, "/dashboard/payment-request"),
contentTheme.info, _FinanceStatItem(LucideIcons.wallet, "Advance Payment",
"/dashboard/expense-main-page", contentTheme.warning, "/dashboard/advance-payment"),
),
_FinanceStatItem(
LucideIcons.receipt_text,
"Payment Request",
"Submit payment requests",
contentTheme.primary,
"/dashboard/payment-request",
),
_FinanceStatItem(
LucideIcons.wallet,
"Advance Payment",
"Manage advance payments",
contentTheme.warning,
"/dashboard/advance-payment",
),
]; ];
final projectSelected = projectController.selectedProject != null; final projectSelected = projectController.selectedProject != null;
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1.1,
),
itemCount: stats.length,
itemBuilder: (context, index) {
return _buildModernFinanceCard(
stats[index],
projectSelected,
index,
);
},
);
}
Widget _buildModernFinanceCard(
_FinanceStatItem statItem,
bool isProjectSelected,
int index,
) {
final bool isEnabled = isProjectSelected;
return TweenAnimationBuilder<double>(
duration: Duration(milliseconds: 400 + (index * 100)),
tween: Tween(begin: 0.0, end: 1.0),
curve: Curves.easeOutCubic,
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: Opacity(
opacity: isEnabled ? 1.0 : 0.5,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _onCardTap(statItem, isEnabled),
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isEnabled
? statItem.color.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
width: 1.5,
),
),
child: Stack(
children: [
// Content
Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
child: Icon(
statItem.icon,
size: 28,
color: statItem.color,
),
),
MySpacing.height(12),
MyText.titleSmall(
statItem.title,
fontWeight: 700,
color: Colors.black87,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
MySpacing.height(4),
if (isEnabled)
Row(
children: [
MyText.bodySmall(
'View Details',
color: statItem.color,
fontWeight: 600,
fontSize: 11,
),
MySpacing.width(4),
Icon(
LucideIcons.arrow_right,
size: 14,
color: statItem.color,
),
],
),
],
),
),
// Lock icon for disabled state
if (!isEnabled)
Positioned(
top: 12,
right: 12,
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(6),
),
child: Icon(
LucideIcons.lock,
size: 14,
color: Colors.grey[600],
),
),
),
],
),
),
),
),
),
);
},
);
}
Widget _buildQuickStatsSection() {
final projectSelected = projectController.selectedProject != null;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleMedium(
'Quick Stats',
fontWeight: 700,
color: Colors.black87,
),
MySpacing.height(4),
MyText.bodySmall(
'Overview of your finances',
color: Colors.grey[600],
),
MySpacing.height(16),
_buildStatsRow(projectSelected),
],
);
}
Widget _buildStatsRow(bool projectSelected) {
final stats = [
_QuickStat(
icon: LucideIcons.trending_up,
label: 'Total Expenses',
value: projectSelected ? '₹0' : '--',
color: contentTheme.danger,
),
_QuickStat(
icon: LucideIcons.clock,
label: 'Pending',
value: projectSelected ? '0' : '--',
color: contentTheme.warning,
),
];
return Row( return Row(
children: stats mainAxisAlignment: MainAxisAlignment.spaceBetween,
.map((stat) => Expanded( children: stats.map((stat) {
return Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
child: _buildStatCard(stat, projectSelected), child: _buildFinanceModuleCard(stat, projectSelected),
), ),
)) );
.toList(), }).toList(),
); );
} }
Widget _buildStatCard(_QuickStat stat, bool isEnabled) { Widget _buildFinanceModuleCard(
return Container( _FinanceStatItem stat, bool isProjectSelected) {
padding: const EdgeInsets.all(16), final bool isEnabled = isProjectSelected;
decoration: BoxDecoration(
color: Colors.white, return Opacity(
borderRadius: BorderRadius.circular(12), opacity: isEnabled ? 1.0 : 0.4,
border: Border.all( child: IgnorePointer(
color: stat.color.withValues(alpha: 0.2), ignoring: !isEnabled,
width: 1, child: InkWell(
), onTap: () => _onCardTap(stat, isEnabled),
), borderRadius: BorderRadius.circular(5),
child: MyCard.bordered(
height: 80,
paddingAll: 5,
borderRadiusAll: 5,
border: Border.all(color: Colors.grey.withOpacity(0.15)),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: stat.color.withValues(alpha: 0.1), color: stat.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(4),
), ),
child: Icon( child: Icon(
stat.icon, stat.icon,
@ -471,119 +171,48 @@ class _FinanceScreenState extends State<FinanceScreen>
color: stat.color, color: stat.color,
), ),
), ),
MySpacing.height(12), MySpacing.height(6),
MyText.bodySmall( Flexible(
stat.label, child: Text(
color: Colors.grey[600], stat.title,
fontSize: 11, textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 12,
overflow: TextOverflow.ellipsis,
),
maxLines: 2,
softWrap: true,
), ),
MySpacing.height(4),
MyText.titleLarge(
stat.value,
fontWeight: 700,
color: isEnabled ? Colors.black87 : Colors.grey[400],
), ),
], ],
), ),
),
),
),
); );
} }
void _onCardTap(_FinanceStatItem statItem, bool isEnabled) { void _onCardTap(_FinanceStatItem statItem, bool isEnabled) {
if (!isEnabled) { if (!isEnabled) {
Get.dialog( Get.defaultDialog(
Dialog( title: "No Project Selected",
shape: RoundedRectangleBorder( middleText: "Please select a project before accessing this section.",
borderRadius: BorderRadius.circular(16), confirm: ElevatedButton(
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.badge_alert,
color: Colors.orange[700],
size: 32,
),
),
MySpacing.height(16),
MyText.titleMedium(
"No Project Selected",
fontWeight: 700,
color: Colors.black87,
textAlign: TextAlign.center,
),
MySpacing.height(8),
MyText.bodyMedium(
"Please select a project before accessing this section.",
color: Colors.grey[600],
textAlign: TextAlign.center,
),
MySpacing.height(24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
style: ElevatedButton.styleFrom( child: const Text("OK"),
backgroundColor: contentTheme.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: MyText.bodyMedium(
"OK",
color: Colors.white,
fontWeight: 600,
),
),
),
],
),
),
), ),
); );
return; } else {
}
Get.toNamed(statItem.route); Get.toNamed(statItem.route);
} }
} }
}
class _FinanceStatItem { class _FinanceStatItem {
final IconData icon; final IconData icon;
final String title; final String title;
final String subtitle;
final Color color; final Color color;
final String route; final String route;
_FinanceStatItem( _FinanceStatItem(this.icon, this.title, this.color, this.route);
this.icon,
this.title,
this.subtitle,
this.color,
this.route,
);
}
class _QuickStat {
final IconData icon;
final String label;
final String value;
final Color color;
_QuickStat({
required this.icon,
required this.label,
required this.value,
required this.color,
});
} }